/*------------------------------------------------------------------------------*
 * File Name: OStat.c: Contains OriginC source code for all OStat features		*
 *	except for Survival Analysis which was moved to SurvivalAnalysis.c.			*
 * Creation: GJL 5/29/2001														*
 * Purpose: Origin C file														*
 * Copyright ( c ) OriginLab Corp.	2000-2002									*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	CPY 7/20/02 v7.0347 OSTAT_COMPILE_PROBLEM_WITH_TYPE_ERROR_MSG				*
 *		I have added Type_ErrorMsg1 function to replace all those that needs	*
 *		second string arguments													*
 *	GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT									*
 *------------------------------------------------------------------------------*/

////////////////////////////////////////////////////////////////////////////////////
//
// System includes
#include <Origin.h>
//#include <OC_const.h>
//#include <string.h>
//#include <data.h>
//#include <math.h>
//#include <utilities.h>
//#include <stdio.h>
//#include <page.h>

// Includes definitions of OStat constants, structures, and non-localized strings.
//  Also includes NAG definitions and prototypes of NAG functions used by OStat.
#include "OStat.h"

// Includes prototypes of Application Utilities.
#include "App_Utils.h"

// Includes definitions of all OStat.c localized strings. $ causes CodeBuilder to look for 
// correct version of Local.h depending on Windows Regional settings
#include "$Local.h"
//
////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
/**
		Computes the lower tail probability of the Student's t-distribution with
		real degrees of freedom. The computational engine is the NAG function
		nag_prob_students_t (g01ebc).
	Example:
		See the [ComputeActualPower] section of tTestMean1Sample.OGS for a sample call.
		Also,
			double dProb;
			dProb = osTTest_ProbT( 2.160, 13 );
			ASSERT( fabs( 0.975 - dProb ) < 0.0005 );
	Parameters:
		dt=Value of t where t can be any Real number
		ddf=Degrees of freedom where ddf > 0
	Return:
		Returns the lower tail probability of the Student's t-distribution for
		parameters dt (value of t) and ddf (degrees of freedom).
*/
double osTTest_ProbT( double dt, int ddf )
{
	double dProbT;
	NagError neErr;					// NAG error structure
	NagError *pneFail = &neErr;
	
	// From <NAG\OCN_g01.h>: g01ebc nag_prob_students_t
	dProbT = nag_prob_students_t( Nag_LowerTail, dt, ddf, pneFail );
	if( pneFail->code != NE_NOERROR )
		return NANUM;				// Return missing value on NAG error
		
	return dProbT;
}

/**
		Function to output to the Results Log a t-Test Table for the One and Two Sample
		t-Test dialog boxes.		.
	Example:
		See the [DisplayTandP] section in TTestMean1Sample.OGS and TTestMean2Sample.OGS for
		sample calls. 
	Parameters:
		iTwoSampleTest=Flag for test type:0 for One Sample t-Test, 1 for Two Sample Independent t-Test, or
			2 for Two Sample Paired t-Test
		dPairedVar=Variance of Paired t-Test
		strNullHypot=Null Hypothesis
		strAltHypot=Alternative Hypothesis
		dt=T value
		iDoF=Degrees of Freedom
		dP=P Value
		iAltRBChoice=Alternative Hypothesis radio button setting
		dSigLevel=Significance Level
		dTestVal=Test Mean for One Sample and Test Difference for Two Sample t-Test
	Return:
		Outputs a t-Test Table for the One and Two Sample t-Test dialog boxes to the Results
		Log and returns OSTAT_NO_ERROR on successful exit or an OSTAT error code on failure.
*/
int	osTTest_Table_Output_to_ResultsLog( int iTwoSampleTest, double dPairedVar, string strNullHypot,
	 string strAltHypot, double dt, int iDoF, double dP, int iAltRBChoice, double dSigLevel, double dTestVal )
{
	// *** If Variance of Paired Two Sample t-Test is 0 abort to prevent division by 0 ***
	if( iTwoSampleTest == 2 && dPairedVar == 0 )
	{
		// Output error message DB and return OSTAT error code
		Type_ErrorMsg( TT2_PAIRED_SD_IS_0_MSG );
		return OSTAT_PAIRED_SD_IS_0_ERROR;
	}
	
	int ii, iSize;
	int iRow, iCol;
	string strOut;
	string strSigLevel, strTestVal;
	string strTablePath;
	string strTableName;
	Worksheet wksTTestTable;
		
	// Create and attach to a temporary t-Test Table output worksheet
	strTablePath = GetFullPath( "osTTestTTable.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksTTestTable.Open( strTablePath, CREATE_TEMP );
	strTableName = wksTTestTable.GetPage().GetName();
	
	// All row and column numbers are indexed from 0
	
	// *** Output Null and Alternative Hypotheses ***
	// Output Null Hypothesis in row 1 and column 4
	iRow = 0;
	iCol = 3;
	wksTTestTable.SetCell( iRow, iCol, strNullHypot );
	
	// Output Alternative Hypothesis in row 2 and column 4
	iRow++;
	wksTTestTable.SetCell( iRow, iCol, strAltHypot );
	
	// *** Output t-Test Table Values t, Dof, and P ***
	// Output t value in row 6 and column 2
	iRow = 5;
	iCol = 1;	
	wksTTestTable.SetCell( iRow, iCol, dt );
	
	// Output DoF in column 3
	iCol++;
	wksTTestTable.SetCell( iRow, iCol, iDoF );
	
	// Output P value in column 4
	iCol++;
	wksTTestTable.SetCell( iRow, iCol, dP );

	// *** Output Decision Rule in Two Phrases ***
	// Localize significance level and the test value
	strSigLevel = LocalizeDouble( dSigLevel, OSTAT_SIG_CON_LEVEL_FORMAT );
	strTestVal = LocalizeDouble( dTestVal, OSTAT_MAX_DIGITS_FORMAT );

	// Output the first phrase of the decision rule in row 9 and column 2
	iRow = 8;
	iCol = 1;
	if( iTwoSampleTest ) // If Two Sample t-Test...
	{
		// Output the first phrase of the Two Sample t-Test decision rule
		strOut.Format( TT2_DECISION_PHRASE1, strSigLevel );
		wksTTestTable.SetCell( iRow, iCol, strOut );
	
		// Output the second phrase of the Two Sample t-Test decision rule in row 10 and column 2
		iRow++;
		switch( iAltRBChoice ) // What Alternative Hypothesis is selected?
		{
			case 1:
				if ( dP > dSigLevel )
					strOut.Format( TT2_DECISION_PHRASE2_IS_NOT_DIFFERENT, strTestVal ); // Is not significantly different
				else 
					strOut.Format( TT2_DECISION_PHRASE2_IS_DIFFERENT, strTestVal );     // Is significantly different
				break;
			case 2:
				if ( dP > dSigLevel )
					strOut.Format( TT2_DECISION_PHRASE2_IS_NOT_GREATER, strTestVal );   // Is not greater
				else 
					strOut.Format( TT2_DECISION_PHRASE2_IS_GREATER, strTestVal );       // Is greater
				break;
			case 3:
				if ( dP > dSigLevel )
					strOut.Format( TT2_DECISION_PHRASE2_IS_NOT_LESS, strTestVal );      // Is not less
				else 
					strOut.Format( TT2_DECISION_PHRASE2_IS_LESS, strTestVal );          // Is less
				break;
		}
		wksTTestTable.SetCell( iRow, iCol, strOut );
	}
	else // Else One Sample t-Test...
	{
		// Output the first phrase of the One Sample t-Test decision rule
		strOut.Format( TT1_DECISION_PHRASE1, strSigLevel );
		wksTTestTable.SetCell( iRow, iCol, strOut );
	
		// Output the second phrase of the One Sample t-Test decision rule in row 10 and column 2
		iRow++;
		switch( iAltRBChoice ) // What Alternative Hypothesis is selected?
		{
			case 1:
				if ( dP > dSigLevel )
					strOut.Format( TT1_DECISION_PHRASE2_IS_NOT_DIFFERENT, strTestVal ); // Is not significantly different
				else 
					strOut.Format( TT1_DECISION_PHRASE2_IS_DIFFERENT, strTestVal );     // Is significantly different
				break;
			case 2:
				if ( dP > dSigLevel )
					strOut.Format( TT1_DECISION_PHRASE2_IS_NOT_GREATER, strTestVal );   // Is not greater
				else 
					strOut.Format( TT1_DECISION_PHRASE2_IS_GREATER, strTestVal );       // Is greater
				break;
			case 3:
				if ( dP > dSigLevel )
					strOut.Format( TT1_DECISION_PHRASE2_IS_NOT_LESS, strTestVal );      // Is not less
				else 
					strOut.Format( TT1_DECISION_PHRASE2_IS_LESS, strTestVal );          // Is less
				break;
		}
		wksTTestTable.SetCell( iRow, iCol, strOut );
	}

	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output t-Test Table worksheet to Results Log
	wksTTestTable.Type( TYPETARGET_OUTPUTLOG, NULL, 5 );

	return OSTAT_NO_ERROR;	
}

/**
		Function to output to the Results Log Confidence Intervals for the One and Two Sample t-Test
		dialog boxes.
	Example:
		See the [DisplayConfidenceIntervals] section in TTestMean1Sample.OGS and TTestMean2Sample.OGS
		for sample calls. 
	Parameters:
		iTwoSampleTest=Flag for test type:0 for One Sample t-Test or 1 for Two Sample t-Test
		strConLevelDataName=Name of dataset containing confidence levels
		strLLimitDataName=Name of dataset containing lower confidence limits
		strULimitDataName=Name of dataset containing upper confidence limits
	Return:
		Outputs Confidence Intervals for the One and Two Sample t-Test dialog boxes to the Results
		Log and returns OSTAT_NO_ERROR on successful exit.
*/
int	osTTest_ConIntervals_Output_to_ResultsLog( int iTwoSampleTest, string strConLevelDataName,
		 string strLLimitDataName, string strULimitDataName )
{
	int ii, iSize;
	string strOut;

	int iRow, iCol;
	string strTablePath;
	string strTableName;
	Worksheet wksConIntTable;

	// Create and attach to a temporary Confidence Interval output worksheet
	strTablePath = GetFullPath( "osConfidenceInterval.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksConIntTable.Open( strTablePath, CREATE_TEMP );
	strTableName = wksConIntTable.GetPage().GetName();
	
	// All row and column numbers are indexed from 0
	
	// *** Change Confidence Interval Main Header if Two Sample Test ***
	if( iTwoSampleTest )
	{
		iRow = 0; // Confidence interval main header in row 1 and column 2
		iCol = 1;
		strOut = TT2_CON_INTERVAL_MAIN_HDR;
		wksConIntTable.SetCell( iRow, iCol, strOut );
	}

	// *** Output Confidence Interval Rows ***	
	// Get confidence levels dataset
	Dataset dsConLevel( strConLevelDataName );
	iSize = dsConLevel.GetSize();
	vector<double> vConLevel;
	vConLevel.SetSize( iSize );
	vConLevel = dsConLevel;
	dsConLevel.Detach();

	// Get lower confidence limits dataset
	Dataset dsLLim( strLLimitDataName );
	iSize = dsLLim.GetSize();
	vector<double> vLLim;
	vLLim.SetSize( iSize );
	vLLim = dsLLim;
	dsLLim.Detach();
	
	// Get upper confidence limits dataset
	Dataset dsULim( strULimitDataName );
	iSize = dsULim.GetSize();
	vector<double> vULim;
	vULim.SetSize( iSize );
	vULim = dsULim;
	dsULim.Detach();
	
	// Insert one blank line in Confidence Interval table for each confidence level
	iRow = 5; // Insert blank lines starting in row 6
	Type_Insert_Blank_Lines_in_Wks( wksConIntTable, iRow, iSize );
		
	// Loop on confidence levels starting in row 6
	for( ii = 0; ii < iSize; iRow++, ii++ )
	{
		// Output confidence level in column 2
		iCol = 1;
		wksConIntTable.SetCell( iRow, iCol, vConLevel[ii] );
			// Output lower limit in column 3
		iCol++;
		wksConIntTable.SetCell( iRow, iCol, vLLim[ii] );
	
		// Output upper limit in column 4
		iCol++;
		wksConIntTable.SetCell( iRow, iCol, vULim[ii] );
	}

	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output Confidence Interval worksheet to Results Log
	wksConIntTable.Type( TYPETARGET_OUTPUTLOG, NULL, 5 );

	return OSTAT_NO_ERROR;	
}

/**
		Function to output to the Results Log Summary Statistics for the One and Two Sample
		t-Test and the One-Way ANOVA dialog boxes.
	Example:
		See the [DisplayTandP] section in TTestMean1Sample.OGS and TTestMean2Sample.OGS, or the 
		the [DisplayFandP] section in ANOVA1Way.OGS for sample calls. 
	Parameters:
		iTest=Flag for test type:0 for One Sample t-Test, 1 for Two Sample Independent t-Test,
			2 for Two Sample Paired t-Test, or 3 for One-Way ANOVA
		nData=Number of datasets to summarize
	Return:
		Outputs Summary Statistics for the One and Two Sample t-Test and the One-Way ANOVA
		dialog boxes to the Results Log and returns OSTAT_NO_ERROR on successful exit.
*/
int	osSummary_Stat_Output_to_ResultsLog( int iTest, int nData )
{
	int ii, iSize;
	double dGetVar;
	char szTemp[40];
	string strDatasetName;
	string strMainHDR;

	// Vector containing names of all selected datasets
	vector<string> vDatasetNames;
	vDatasetNames.SetSize( nData );

	// Vector containing the number of datapoints in each selected dataset
	vector<int> vNPTS;
	vNPTS.SetSize( nData );

	// Vector containing the mean of each selected dataset
	vector<double> vMEAN;
	vMEAN.SetSize( nData );

	// Vector containing the standard deviation of each selected dataset
	vector<double> vSD;
	vSD.SetSize( nData );

	// Vector containing the standard error of each selected dataset
	vector<double> vSE;
	vSE.SetSize( nData );

	// *** Construct All Test Specific Strings and Vectors needed for Summary Table ***
	// Default value of case 2
	strMainHDR = TT2_PAIRED_SUMMARY_MAIN_HDR;
	switch( iTest )
	{
		case 0:
			// One Sample tTest...get summary statistics for One Sample t-Test
			strMainHDR = TT1_SUMMARY_MAIN_HDR;
			LT_get_str( "%B", szTemp, 40 ); strDatasetName = szTemp;
			vDatasetNames[0] = strDatasetName; 
			LT_evaluate( "sum.n", &dGetVar ); vNPTS[0] = (int) dGetVar;
			LT_evaluate( "sum.mean", &dGetVar ); vMEAN[0] = dGetVar;
			LT_evaluate( "sum.sd", &dGetVar ); vSD[0] = dGetVar;
			LT_evaluate( "sum.sd/sqrt(sum.n)", &dGetVar ); vSE[0] = dGetVar;
			break;
		case 1:
			// Two Sample Independent tTest...continue on through case 2
			strMainHDR = TT2_INDEPENDENT_SUMMARY_MAIN_HDR;
		case 2:
			// Two Sample Paired tTest...code common with case 1
			// Get summary statistics for the first selected dataset of the Two Sample t-Test
			LT_get_str( "%A", szTemp, 40 ); strDatasetName = szTemp;
			vDatasetNames[0] = TT2_SAMPLE1 + strDatasetName; 
			LT_get_var( "n1", &dGetVar ); vNPTS[0] = (int) dGetVar;
			LT_get_var( "ave1", &dGetVar ); vMEAN[0] = dGetVar;
			LT_get_var( "sd1", &dGetVar ); vSD[0] = dGetVar;
			LT_get_var( "se1", &dGetVar ); vSE[0] = dGetVar;

			// Get summary statistics for the second selected dataset of the Two Sample t-Test
			LT_get_str( "%B", szTemp, 40 ); strDatasetName = szTemp;
			vDatasetNames[1] = TT2_SAMPLE2 + strDatasetName; 
			LT_get_var( "n2", &dGetVar ); vNPTS[1] = (int) dGetVar;
			LT_get_var( "ave2", &dGetVar ); vMEAN[1] = dGetVar;
			LT_get_var( "sd2", &dGetVar ); vSD[1] = dGetVar;
			LT_get_var( "se2", &dGetVar ); vSE[1] = dGetVar;
			break;
		case 3:
			// One-Way ANOVA
			strMainHDR = AN1_SUMMARY_MAIN_HDR;
			
			// Get summary statistics for all selected ANOVA1 datasets
			Dataset dsNPTS( "_NPTS" ); vNPTS = dsNPTS; dsNPTS.Detach();
			Dataset dsMEAN( "_MEAN" ); vMEAN = dsMEAN; dsMEAN.Detach();
			Dataset dsSD( "_SD" ); vSD = dsSD; dsSD.Detach();
			double dNpts;
			string strDB;
						
			// Read the names of all selected datasets...
			for( ii = 0; ii < nData; ii++ )
			{
				dNpts = (double) vNPTS[ii];
				vSE[ii] = vSD[ii] / sqrt( dNpts );
			
				strDB.Format( "%%A=ANOVA1Way!SelectedDataLBX.v%d$", ii + 1 );
				LT_execute( strDB );
				LT_get_str( "%A", szTemp, 40 );
				strDatasetName = szTemp;
				vDatasetNames[ii] = strDatasetName;
			}
			
			strDatasetName = AN1_DATASET;
			break;
	}

	int iRow, iCol;
	string strTablePath;
	string strTableName;
	Worksheet wksSummaryTable;
		
	// Create and attach to a temporary Summary Statistics output worksheet
	strTablePath = GetFullPath( "osSummaryStat.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksSummaryTable.Open( strTablePath, CREATE_TEMP );
	strTableName = wksSummaryTable.GetPage().GetName();

	// All row and column numbers are indexed from 0
	
	// *** Change Main Header ***
	// Change main header in row 1 and column 1
	iRow = 0;
	iCol = 0;
	wksSummaryTable.SetCell( iRow, iCol, strMainHDR );
	
	if( iTest==3 ) // If One-Way ANOVA...
	{
		// Change "Sample" to "Dataset" in row 5 column 2
		iRow = 4;
		iCol = 1;
		wksSummaryTable.SetCell( iRow, iCol, strDatasetName );
	}
	
	// ***  Add nData Summary Rows and Output Summary Statistics *** 
	// Insert nData blank lines in summary table at row 7
	iRow = 6;
	Type_Insert_Blank_Lines_in_Wks( wksSummaryTable, iRow, nData );
		
	// Loop on samples/selected datasets starting in row 7
	for( ii = 0; ii < nData; iRow++, ii++ )
	{
		// Output sample or selected dataset name in column 2
		iCol = 1;
		strDatasetName = vDatasetNames[ii];
		wksSummaryTable.SetCell( iRow, iCol, strDatasetName );
		
		// Output number of data points in column 3
		iCol++;
		wksSummaryTable.SetCell( iRow, iCol, vNPTS[ii] );
	
		// Output mean in column 4
		iCol++;
		wksSummaryTable.SetCell( iRow, iCol, vMEAN[ii] );

		// Output standard deviation in column 5
		iCol++;
		wksSummaryTable.SetCell( iRow, iCol, vSD[ii] );
	
		// Output standard error in column 6
		iCol++;
		wksSummaryTable.SetCell( iRow, iCol, vSE[ii] );
	}
	
	if( mod( iTest, 3 ) != 0 ) // If Two Sample t-Test (iTest=1 or 2)...
	{
		// Add blank row after separator table
		iRow++;
		Type_Insert_Blank_Lines_in_Wks( wksSummaryTable, iRow );
		
		// Output difference of sample means after separator table
		iCol = 1;
		wksSummaryTable.SetCell( iRow, iCol, TT2_DIF_OF_MEANS );
		iCol = 3;
		wksSummaryTable.SetCell( iRow, iCol, vMEAN[0] - vMEAN[1] );
	}

	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output Summary Statistics worksheet to Results Log
	wksSummaryTable.Type( TYPETARGET_OUTPUTLOG, NULL, 7 );

	return OSTAT_NO_ERROR;	
}

/**
		Function to output to the Results Log Actual and Hypothetical Power values for the
		One and Two Sample t-Test and the One-Way ANOVA dialog boxes.
	Example:
		See the [DisplayPower] section in TTestMean1Sample.OGS, TTestMean2Sample.OGS, and
		ANOVA1Way.OGS for sample calls. 
	Parameters:
		iPower=Bitwise flag indicating whether no power (0), only actual power (1), or actual and
			hypothetical powers (3) are to be output
		dAlpha=Alpha used in power computations
		iSSType=Flag for sample size type:0 for "Total" or 1 for "Individual"
		iActualSS=Actual sample size
		dActualPower=Actual power value
		strHypotSSDataName=Name of dataset containing hypothetical sample sizes
		strHypotPowerDataName=Name of dataset containing hypothetical powers
	Return:
		Outputs Actual and Hypothetical Power values to Results Log for the One and
		Two Sample t-Test and the One-Way ANOVA dialog boxes. Returns OSTAT_NO_ERROR
		on successful exit.
*/
int osPower_Output_to_ResultsLog( int iPower, double dAlpha, int iSSType, int iActualSS,
	 double dActualPower, string strHypotSSDataName, string strHypotPowerDataName )
{
	int ii, iSize;
	int iRow, iCol;
	string strTablePath;
	string strTableName;
	Worksheet wksPowerTable;

	// Create and attach to a temporary Power output worksheet
	strTablePath = GetFullPath( "osPowerAnalysis.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksPowerTable.Open( strTablePath, CREATE_TEMP );
	strTableName = wksPowerTable.GetPage().GetName();
	
	// All row and column numbers are indexed from 0
	
	// *** Finish Power Header ***
	iRow = 2; // Sample size type in row 3
	switch( iSSType )
	{
		case -1:
			// If One Sample t-Test then no sample size type row...delete row in *.OGW
			wksPowerTable.DeleteRow( iRow );
			iRow--;
			break;

		case 0:
			// If Two Sample Independent t-Test then sample size type is Total (most common)
			// so leave alone...Total is in *.OGW
			break;

		case 1:
			// If Two Sample Paired t-Test then sample size type is Individual so overwrite
			// Total with Individual
			iCol = 2; // Output "Individual" in column 3
			wksPowerTable.SetCell( iRow, iCol, TT2_INDIVIDUAL );
			break;
	}

	// *** Output Actual Power Row ***	
	// Insert one blank line in power table for actual power
	iRow += 3; // Jump to row for actual power
	Type_Insert_Blank_Lines_in_Wks( wksPowerTable, iRow );

	// Output actual alpha in column 2
	iCol = 1;
	wksPowerTable.SetCell( iRow, iCol, dAlpha );

	// Output actual sample size in column 3
	iCol++;
	wksPowerTable.SetCell( iRow, iCol, iActualSS );
		
	// Output actual power in column 4
	iCol++;
	wksPowerTable.SetCell( iRow, iCol, dActualPower );
	
	// Output "(actual)" in column 5
	iCol++;
	wksPowerTable.SetCell( iRow, iCol, OSTAT_POWER_ACTUAL );

	// *** Output Hypothetical Power Rows ***	
	if( iPower & 2 ) // If hypothetical powers were computed...
	{
		// Get hypothetical power dataset
		Dataset dsSSPOW( strHypotPowerDataName );
		iSize = dsSSPOW.GetSize();
		vector<double> vSSPOW;
		vSSPOW.SetSize( iSize );
		vSSPOW = dsSSPOW;
		dsSSPOW.Detach();
		
		// Get hypothetical sample size dataset
		Dataset dsSS( strHypotSSDataName );
		iSize = dsSS.GetSize(); // Get number of hypothetical sample sizes
		vector<int> vSS;
		vSS.SetSize( iSize );
		vSS = dsSS;
		dsSS.Detach();	
		
		// Insert one blank line in power table for each hypothetical power
		iRow++;
		Type_Insert_Blank_Lines_in_Wks( wksPowerTable, iRow, iSize );
			
		// Loop on hypothetical sample sizes starting in row 7
		for( ii = 0; ii < iSize; iRow++, ii++ )
		{
			// Output alpha in column 2
			iCol = 1;
			wksPowerTable.SetCell( iRow, iCol, dAlpha );

			// Output hypothetical sample size in column 3
			iCol++;
			wksPowerTable.SetCell( iRow, iCol, vSS[ii] );
		
			// Output hypothetical power in column 4
			iCol++;
			wksPowerTable.SetCell( iRow, iCol, vSSPOW[ii] );
		}
	}

	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output Power worksheet to Results Log
	wksPowerTable.Type( TYPETARGET_OUTPUTLOG, NULL, 5 );	

	return OSTAT_NO_ERROR;	
}

/**
		Computes the power of an ANOVA experiment.  The computational engine is the
		NAG function nag_prob_non_central_f_dist.
	Example:
		See the [ComputeActualPower] section of ANOVA1Way.OGS and the osANOVA2Way_Compute_Power
		function for sample calls.
	Parameters:
		dFval=Deviate from the F-distribution for given values of alpha, df1, and df2
		ddf1=Degrees of freedom for the numerator variance
		ddf2=Degrees of freedom for the denominator variance
		dnc=Non-centality factor
	Return:
		Returns the power of an ANOVA experiment on successful exit or NANUM on error.
*/
double osANOVA_Power( double dFval, double ddf1, double ddf2, double dnc )
{
	double dtol = AN_POWER_TOLERANCE;		// Required accuracy of the solution
	int imax_iter = AN_POWER_ITERATIONS;	// Maximum number of iterations to be performed
	NagError neErr;							// NAG error structure
	NagError *pneFail = &neErr;
	
	double dPower;							// Computed power
	
	// From <NAG\OCN_g01.h>: g01gdc nag_prob_non_central_f_dist	
	// Computes probabilities for the non-central F-distribution
	dPower = 1 - nag_prob_non_central_f_dist( dFval, ddf1, ddf2, dnc, dtol, imax_iter, pneFail );
	if( pneFail->code != NE_NOERROR )
	{											// If NAG error = NE_REAL_ARG_CONS (non-centrality
		if( pneFail->code == NE_REAL_ARG_CONS ) //  factor is too large causing nag_prob_non_central_f_dist
			dPower = 1;							//  to effectively return 0) then return 1. Otherwise,
		else									//  return NANUM on NAG error.
			dPower = NANUM;
	}
	
	return dPower;
}

/**
		Computes the standard errors of the differences of the means for the osANOVA1Way_Means_Comparison
		and osANOVA2Way_Means_Comparison functions. The returned vector vC is used in place of the C
		matrix required by the NAG function nag_anova_confid_interval and consequently is indexed as
		if it were a matrix (using row major order).
	Example:
		See functions osANOVA1Way_Means_Comparison and osANOVA2Way_Means_Comparison.
	Parameters:
		inData=Number of datasets for which the standard errors of the differences of the means is
			computed
		dmse=Mean Square Error of ANOVA computation
		vNPTS=A vector containing the number of datapoints in each of the inData datasets
		vC=The returned vector vC contains standard errors of the differences of the means for an
			ANOVA computation
	Return:
		Returns a vector vC used in place of the C matrix required by the NAG function
		nag_anova_confid_interval. The vector vC contains standard errors of the differences of the
		means for an ANOVA computation. The jj + ii * inData element contains the standard error
		of the difference between the ii^th and jj^th means. Also returns OSTAT_NO_ERROR on successful
		exit.  
*/
int osANOVA_Compute_StdErr_MeanDiffs( int inData, double dmse, vector<int> &vNPTS, vector<double> &vC )
{
	int ii, jj; 
	
	// Loop through lower triangle of "matrix" indexing through vector as if it were a matrix
	//  For inData == 3, Non-zero numbers identify vector index for elements in Lower triangle of matrix:
	//     index = Col + Row * NumCols:                     O O O
	//														3 O O
	//														6 7 O
	
	for( ii = 1; ii < inData; ii++ )			// Loop on rows of "matrix" starting with second row (ii==1)
	{
		for( jj = 0; jj < ii; jj++ )			// Loop on columns of "matrix"
		{
			// Compute standard errors of the differences of the means 
			vC[jj + ii * inData] = sqrt( dmse * ( 1.0 / vNPTS[ii] + 1.0 / vNPTS[jj] ) );
		}
	}

	return OSTAT_NO_ERROR;
}

////////////////////////////////////////////////////////////////////////////////////
/**
		Function to output to the Results Log an F Table for the One-Way ANOVA Test or
		for the Levene and Brown-Forsythe Tests of Equal Variance.		.
	Example:
		See the [DisplayFandP] section in ANOVA1Way.OGS for a sample call. 
	Parameters:
		strTestType=Test Type: ANOVA on "RawData", or "Levene" or "BrownForsythe" Tests of
			Equal Variance
		ddf1=Degrees of Freedom of Model row
		dssb=Sum of Squares of Model row
		dmsb=Mean Square of Model row
		dF=F Value 
		dP=P Value
		ddf2=Degrees of Freedom of Error row
		dsse=Sum of Squares of Error row
		dmse=Mean Square of Error row
		dSigLevel=Significance Level 
	Return:
		Outputs an F Table for the One-Way ANOVA Test or for the Levene or Brown-Forsythe
		Tests of Equal Variance and returns OSTAT_NO_ERROR on successful exit.
*/
int	osANOVA1_Table_Output_to_ResultsLog( string strTestType, double ddf1, double dssb, double dmsb,
	 double dF, double dP, double ddf2, double dsse, double dmse, double dSigLevel )
{
	int ii, iRow, iCol;
	string strOut;
	string strSigLevel;
	string strTablePath;
	string strTableName;
	Worksheet wksANOVA1Table;
		
	// Create and attach to a temporary ANOVA1 Table output worksheet
	strTablePath = GetFullPath( "osANOVA1Table.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksANOVA1Table.Open( strTablePath, CREATE_TEMP );
	strTableName = wksANOVA1Table.GetPage().GetName();
	
	// All row and column numbers are indexed from 0
	
	// *** If Needed Delete Null and Alternative Hypothesis Rows and Change Test Header ***
	if( strTestType.Compare( "RawData" ) ) // If test is not "RawData"...
	{
		// Delete first three rows of temporary worksheet containing hypotheses
		iRow = 0;
		for( ii = 0; ii < 3; ii++ )
			wksANOVA1Table.DeleteRow( iRow );

		// Change Test Header in row 1 and column 2
		iCol = 1;
		if( strTestType.Compare( "Levene" ) )
			strOut = AN1_BROWN_FORSYTHE_HDR; // Returns 0 on match...so if "not" "Levene"
		else
			strOut = AN1_LEVENE_HDR; // Else is "Levene"
		wksANOVA1Table.SetCell( iRow, iCol, strOut );
				
		iRow = 5; // Model row number if hypotheses are deleted 
	}
	else // Else test is "RawData" skip to Model row of F table
		iRow = 8; // Model row number if hypotheses are not deleted 

	// *** Output ANOVA Table Values DoF, Sum of Squares, Mean Square, F, and P ***
	// Output values in Model row of ANOVA Table
	// Output df1 value in column 3
	iCol = 2;	
	wksANOVA1Table.SetCell( iRow, iCol, ddf1 );
	
	// Output ssb in column 4
	iCol++;
	wksANOVA1Table.SetCell( iRow, iCol, dssb );
	
	// Output msb value in column 5
	iCol++;	
	wksANOVA1Table.SetCell( iRow, iCol, dmsb );
	
	// Output F in column 6
	iCol++;
	wksANOVA1Table.SetCell( iRow, iCol, dF );
	
	// Output P value in column 7
	iCol++;
	wksANOVA1Table.SetCell( iRow, iCol, dP );
	
	// Output values in Error row of ANOVA Table
	// Output df2 value in next row and column 3
	iRow++;
	iCol = 2;	
	wksANOVA1Table.SetCell( iRow, iCol, ddf2 );
	
	// Output sse in column 4
	iCol++;
	wksANOVA1Table.SetCell( iRow, iCol, dsse );
	
	// Output mse value in column 5
	iCol++;	
	wksANOVA1Table.SetCell( iRow, iCol, dmse );
	
	// *** Output Decision Rule ***
	
	// Localize significance level
	strSigLevel = LocalizeDouble( dSigLevel, OSTAT_SIG_CON_LEVEL_FORMAT );

	// Output the first phrase of the decision rule three rows after the Model row and in column 2
	iRow += 3;
	iCol = 1;
	strOut.Format( AN1_DECISION_PHRASE1, strSigLevel );
	wksANOVA1Table.SetCell( iRow, iCol, strOut );
	
	//  Output second phrase of decision rule in next row and in column 2
	iRow++;
	// .Compare returns 0 on match so if test type is not "RawData" output decision rule for population variances
	if( strTestType.Compare( "RawData" ) )
	{
		if ( dP > dSigLevel )
			strOut = AN1_DECISION_PHRASE2_VARS_ARE_NOT_DIFF; // Variances are not different
		else 
			strOut = AN1_DECISION_PHRASE2_VARS_ARE_DIFF;     // Variances are different
	}
	else // Else test type is "RawData" output decision rule for population means
	{
		if ( dP > dSigLevel )
			strOut = AN1_DECISION_PHRASE2_MEANS_ARE_NOT_DIFF; // Means are not different
		else 
			strOut = AN1_DECISION_PHRASE2_MEANS_ARE_DIFF;     // Means are different
	}
	wksANOVA1Table.SetCell( iRow, iCol, strOut ); // Output the second phrase of the decision rule

	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output ANOVA1 Table worksheet to Results Log
	wksANOVA1Table.Type( TYPETARGET_OUTPUTLOG, NULL, 8 );

	return OSTAT_NO_ERROR;	
}

/**
		Function to compute and output the One-Way ANOVA Means Comparison analysis. For each selected
		means comparison test (Bonferroni, Scheffe', or Tukey), the mean of each selected dataset is
		compared to the mean of every other selected dataset. The computational engine for performing
		the means comparison tests is the NAG function nag_anova_confid_interval (from <NAG\OCN_g04.h>:
		g04dbc)
	Example:
		See the [MeansComparison] section in ANOVA1Way.OGS for a sample call. 
	Parameters:
		iTest=Bitwise flag indicating which Means Comparison tests to perform: Bonferroni (Bit 0),
			Scheffe' (Bit 1), or Tukey (Bit 2)
		inData=Number of selected datasets
		strNPTS=Name of dataset holding the number of data points in each selected dataset
		ddf2=Degrees of Freedom of the ANOVA Error term
		strMEAN=Name of dataset holding the mean of each selected dataset
		dmse=Mean Square of the ANOVA Error term
		dSigLevel=Significance Level
	Return:
		For each selected means comparison test (Bonferroni, Scheffe', or Tukey), the mean of each
		selected dataset is compared to the mean of every other selected dataset. Returns
		OSTAT_NO_ERROR on successful exit.
*/
int osANOVA1Way_Means_Comparison( int iTest, int inData, string strNPTS, double ddf2, string strMEAN,
	 double dmse, double dSigLevel )
{
	int iErr, iSize;
	string strTestName; // Name of test to perform
	
	// Read in the NPTS dataset
	Dataset dsNPTS( strNPTS );
	iSize = dsNPTS.GetSize();
	vector<int> vNPTS;
	vNPTS.SetSize( iSize );
	vNPTS = dsNPTS;
	dsNPTS.Detach();	
	
	// Read in the MEAN dataset
	Dataset dsMEAN( strMEAN );
	iSize = dsMEAN.GetSize();
	vector<double> vMEAN;
	vMEAN.SetSize( iSize );
	vMEAN = dsMEAN;
	dsMEAN.Detach();

	// Vector to hold the standard errors of the differences of the means 	
	vector<double> vC;
	iSize = pow( inData, 2 );
	vC.SetSize( iSize );
	
	// Function computes the standard errors of the differences of the means 
	iErr = osANOVA_Compute_StdErr_MeanDiffs( inData, dmse, vNPTS, vC );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;

	double dCLevel;
	dCLevel = 1 - dSigLevel; // Confidence level

	// Vector to hold the lower limit of the simultaneous confidence interval of the differences of the means
	iSize = inData * ( inData - 1 ) / 2;
	vector<double> vCIL;
	vCIL.SetSize( iSize );
	
	// Vector to hold the upper limit of the simultaneous confidence interval of the differences of the means
	vector<double> vCIU;
	vCIU.SetSize( iSize );

	// Vector to hold Significance decisions: 1=YES and 0=NO	
	vector<int> vISIG;
	vISIG.SetSize( iSize );

	// Perform Bonferroni test?
	if( iTest & 1 )
	{
		strTestName = AN_BONFERRONI_TEST;

		// From <NAG\OCN_g04.h>: g04dbc nag_anova_confid_interval: Perform Bonferroni test
		iErr = nag_anova_confid_interval( Nag_BonferroniInterval, inData, vMEAN, ddf2, vC, inData, dCLevel, vCIL,
			 vCIU, vISIG );
		if( iErr != NE_NOERROR )
			return OSTAT_CONF_INT_ERROR1;

		// Output the means comparison analysis for the Bonferroni test
		iErr = osANOVA1Way_Means_Comparison_Output_to_ResultsLog( strTestName, inData, vMEAN, vCIL,
			 vCIU, vISIG, dSigLevel );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	}			

	// Perform Scheffe' test?
	if( iTest & 2 )
	{
		strTestName = AN_SCHEFFE_TEST;

		// From <NAG\OCN_g04.h>: g04dbc nag_anova_confid_interval: Perform Scheffe' test
		iErr = nag_anova_confid_interval( Nag_ScheffeInterval, inData, vMEAN, ddf2, vC, inData, dCLevel, vCIL,
			 vCIU, vISIG );
		if( iErr != NE_NOERROR )
			return OSTAT_CONF_INT_ERROR2;

		// Output the means comparison analysis for the Scheffe' test
		iErr = osANOVA1Way_Means_Comparison_Output_to_ResultsLog( strTestName, inData, vMEAN, vCIL,
			 vCIU, vISIG, dSigLevel );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	}
	
	// Perform Tukey test?
	if( iTest & 4 )
	{
		strTestName = AN_TUKEY_TEST;

		// SET_MAX_TUKEY_RESID_DF QA70-688: Limit max value of ddf2 since NAG function nag_anova_confid_interval
		// crashes for Tukey test when ddf2 > AN_MAX_TUKEY_RESID_DF. There is little difference in function values
		// when ddf2 is large (>120) so set AN_MAX_TUKEY_RESID_DF as max value for ddf2 
		if( ddf2 > AN_MAX_TUKEY_RESID_DF )
			ddf2 = AN_MAX_TUKEY_RESID_DF;
		// END SET_MAX_TUKEY_RESID_DF QA70-688

		// From <NAG\OCN_g04.h>: g04dbc nag_anova_confid_interval: Perform Tukey test
		iErr = nag_anova_confid_interval( Nag_TukeyInterval, inData, vMEAN, ddf2, vC, inData, dCLevel, vCIL,
			 vCIU, vISIG );
		if( iErr != NE_NOERROR )
			return OSTAT_CONF_INT_ERROR3;

		// Output the means comparison analysis for the Tukey test
		iErr = osANOVA1Way_Means_Comparison_Output_to_ResultsLog( strTestName, inData, vMEAN, vCIL,
			 vCIU, vISIG, dSigLevel );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	}

	return OSTAT_NO_ERROR;
}

/**
		One-Way ANOVA function to output Means Comparison results to the Results Log.
	Example:
		See function osANOVA1Way_Means_Comparison.
	Parameters:
		strTestName=Name of Means Comparison test to perform: Bonferroni, Scheffe', or Tukey
		inData=Number of selected datasets
		vMEAN=Means of selected datasets 
		vCIL=Lower limit of simultaneous confidence intervals for difference of means
		vCIU=Upper limit of simultaneous confidence intervals for difference of means
		vISIG=Significance decisions: 1=YES and 0=NO
		dSigLevel=Significance Level
	Return:
		Outputs One-Way ANOVA Means Comparison results to Results Log. Returns OSTAT_NO_ERROR
		on successful exit.
*/
int osANOVA1Way_Means_Comparison_Output_to_ResultsLog( string strTestName, int inData, vector<double> &vMEAN,
	 vector<double> &vCIL, vector<double> &vCIU, vector<int> &vISIG, double dSigLevel )
{
	int ii, jj, iv;
	int iRow, iCol;
	double dMeanDif;
	string strDB, strOut;
	string strDatasetName, strSigLevel, strSig;
	char szTemp[40];
	
	string strTablePath;
	string strTableName;
	Worksheet wksMeansTable;
	
	// Transpose (negate and swap) vCIU and vCIL to standardize NAG output with references
	vector<double> vCILT;
	vCILT.SetSize( inData );
	vCILT = -1 * vCIU;

	vector<double> vCIUT;
	vCIUT.SetSize( inData );
	vCIUT = -1 * vCIL;

	// Create and attach to a temporary One-Way ANOVA Means Comparison output worksheet
	strTablePath = GetFullPath( "osANOVA1Means.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksMeansTable.Open( strTablePath, CREATE_TEMP );
	strTableName = wksMeansTable.GetPage().GetName();
	
	// Rows and columns are indexed from 0
	
	// Output Test name in Means Comparison header in
	iRow = 0; // row 1 and column 2 of worksheet
	iCol = 1;
	strOut.Format( AN1_MEANS_COMP_HDR1, strTestName );
	wksMeansTable.SetCell( iRow, iCol, strOut );
	
	// Localize significance level and the test value
	strSigLevel = LocalizeDouble( dSigLevel, OSTAT_SIG_CON_LEVEL_FORMAT );

	// Output Significance Level in Means Comparison header in
	iRow = 3; // row 4 and column 7 of worksheet
	iCol = 6;
	strOut.Format( AN_MEANS_COMP_AT_LEVEL, strSigLevel );
	wksMeansTable.SetCell( iRow, iCol, strOut );	

	// Loop on all selected datasets
	for( jj = 1; jj < inData; jj++ )
	{
		// Compute row of current dataset
		iRow++;
		if( jj != 1 ) //  For all but first dataset...
		{
			Type_Blank_Lines_To_Wks( wksMeansTable, iRow, 2 ); // Blank out 2 rows and skip one blank row
			iRow++;
		}
		
		// Output current dataset name in column 2 in row of current dataset 
		iCol = 1;
		strDB.Format( "%%A=ANOVA1Way!SelectedDataLBX.v%d$", jj );
		LT_execute( strDB );
		LT_get_str( "%A", szTemp, 40 );
		strDatasetName = szTemp;
		wksMeansTable.SetCell( iRow, iCol, strDatasetName );
		
		// Output mean of current dataset in column 3 in row of current dataset 
		iCol++;
		wksMeansTable.SetCell( iRow, iCol, vMEAN[jj - 1] );
		
		// Output separator line
		iRow++;
		Type_Separator_Line_To_Wks( wksMeansTable, iRow );
		
		for( ii = jj + 1; ii <= inData; ii++ )
		{
			// Compute row of comparison dataset and initially blank row out
			iRow++;
			Type_Blank_Lines_To_Wks( wksMeansTable, iRow, 1 );
			
			// Output comparison dataset name in column 2 in row of comparison dataset
			iCol = 1;
			strDB.Format( "%%A=ANOVA1Way!SelectedDataLBX.v%d$", ii );
			LT_execute( strDB );
			LT_get_str( "%A", szTemp, 40 );
			strDatasetName = szTemp;
			wksMeansTable.SetCell( iRow, iCol, strDatasetName );
			
			// Output mean of comparison dataset in column 3 in row of comparison dataset 
			iCol++;
			wksMeansTable.SetCell( iRow, iCol, vMEAN[ii - 1] );

			// Output the difference of the mean of the current dataset minus the mean of the comparison
			//  dataset in column 4
			iCol++;
			dMeanDif = vMEAN[jj - 1] - vMEAN[ii - 1];
			wksMeansTable.SetCell( iRow, iCol, dMeanDif );

			// Compute index into confidence limit and significance vectors			
			iv = ( ( ii - 1 ) * ( ii - 2 ) ) / 2 + ( jj - 1 );
			
			// Output lower confidence limit for difference of means in column 5
			iCol++;
			wksMeansTable.SetCell( iRow, iCol, vCILT[iv] );

			// Output upper confidence limit for difference of means in column 6
			iCol++;
			wksMeansTable.SetCell( iRow, iCol, vCIUT[iv] );

			// Output significance decision YES or NO in column 7
			iCol++;
			if( vISIG[iv] == 1 )
				strOut = AN_SIGNIFICANT_YES;
			else
				strOut = AN_SIGNIFICANT_NO;
			wksMeansTable.SetCell( iRow, iCol, strOut );
		}

		// Output separator line
		iRow++;
		Type_Separator_Line_To_Wks( wksMeansTable, iRow );
	}
		
	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output Means Comparison worksheet to Results Log
	wksMeansTable.Type( TYPETARGET_OUTPUTLOG, NULL, 8 );

	return OSTAT_NO_ERROR;
}

////////////////////////////////////////////////////////////////////////////////////
/**
		Main Two-Way ANOVA function used to compute and output the Two-Way ANOVA
		table, Means Comparison, and Power analysis.
	Example:
		See the [ANOVA2Way] section in the ANOVA2Way.OGS file.
	Parameter:
		dSigLevel=Significance Level
		iInteractions=Enable (1) or disable (0) computation of Two-Way ANOVA interactions
		iSpecifyLevelsBy=Method of specifying levels. By Dataset=1, by Classification
			Variable=2
		iN=Number of selected datasets
		iPower=Bitwise integer specifying whether or not to compute actual and hypothetical
			Power: Actual=1, Hypothetical=2
		dPowerAlpha=Alpha used to compute Power
		strSampleSize=Name of dataset holding comma separated list of hypothetical sample sizes
		iTest=Bitwise integer specifying which Means Comparison test(s) if any to run:
			Bonferroni=1, Scheffe=2, Tukey=4
	Return:
		Outputs the Two-Way ANOVA table, Means Comparison table, and Power Analysis table
		to the Results Log. Returns OSTAT_NO_ERROR on successful exit or an OSTAT error
		code on failure.
*/
int osANOVA2Way( double dSigLevel, int iInteractions, int iSpecifyLevelsBy, int nData, int iPower, double dPowerAlpha,
	 string strSampleSizes, int iTest )
{
	int iErr, iANOVAErr;
	int nLevelsA, nLevelsB, nPts;
	char cFactor;
	string strErrMsg;
	
	// Vector to hold names of selected datasets
	vector<string> vDataNames;
	vDataNames.SetSize( nData );

	// Vectors to hold integral indexes to levels of Factors A and B
	vector<int> vFACTOR_A;
	vector<int> vFACTOR_B;
	
	// Vector to hold the values of the dependent variable
	vector<double> vDEP_VAR;
	
	// Vectors to hold the text values of the levels of Factors A and B 
	vector<string> vLEVELSA;
	vector<string> vLEVELSB;

	// A four element array of an ANOVA2_Row structure to hold ANOVA2 Table
	ANOVA2_Row arANOVA2_Table[4];

	// Get all datasets needed from the Origin project file, combine individual datasets into factor A and factor B
	// vectors (if needed), and return the number of levels and the number of data points in each factor
	iErr = osANOVA2Way_Get_Data( iSpecifyLevelsBy, nData, vDataNames, vFACTOR_A, vLEVELSA, nLevelsA, vFACTOR_B, vLEVELSB,
		 nLevelsB, vDEP_VAR, nPts );
	if( iErr != OSTAT_NO_ERROR )	// If error getting data return without performing ANOVA
		return iErr;

	// Factor A must have at least 2 levels, if not display error message and return
	if( nLevelsA < 2 )
	{																		
		Type_ErrorMsg( AN2_TOO_FEW_LEVELSA );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}

	// Factor B must have at least 2 levels, if not display error message and return	
	if( nLevelsB < 2 )
	{
		Type_ErrorMsg( AN2_TOO_FEW_LEVELSB );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	
	// Compute all values displayed in Two-Way ANOVA table 
	iANOVAErr = osANOVA2Way_Compute_ANOVA_Table( iInteractions, vFACTOR_A, nLevelsA, vFACTOR_B, nLevelsB, vDEP_VAR, nPts, &arANOVA2_Table[0] );

	// Output Two-Way ANOVA table to Results Log even if there were errors (still may be useful information)
	iErr = osANOVA2Way_ANOVA_Table_Output_to_ResultsLog( iSpecifyLevelsBy, nData, vDataNames, iInteractions, &arANOVA2_Table[0] );
	
	// If errors found when computing ANOVA table then exit after outputing ANOVA table and error message
	if( iANOVAErr != OSTAT_NO_ERROR )
	{
		strErrMsg.Format( AN2_COMPUTATIONAL_ERROR_MSG, iANOVAErr ); 
		Type_ErrorMsg( strErrMsg );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	
	// If errors found when outputing ANOVA table then exit 
	if( iErr != OSTAT_NO_ERROR )
		return iErr;

	// If Two-Way ANOVA Means Comparison is requested...
	if( iTest )
	{
		// Perform Means Comparison of Factor A Levels, output to Results Log, and return if error
		cFactor = 'A';
		iErr = osANOVA2Way_Means_Comparison( cFactor, iTest, vFACTOR_A, vLEVELSA, nLevelsA, vDEP_VAR, nPts,
			arANOVA2_Table[3].dDF, arANOVA2_Table[3].dMS, dSigLevel );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;	

		// Perform Means Comparison of Factor B Levels, output to Results Log, and return if error
		cFactor = 'B';
		iErr = osANOVA2Way_Means_Comparison( cFactor, iTest, vFACTOR_B, vLEVELSB, nLevelsB, vDEP_VAR, nPts,
			arANOVA2_Table[3].dDF, arANOVA2_Table[3].dMS, dSigLevel );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	}

	// If Actual Power computation is requested...
	if( iPower & 1 )
	{
		// Perform Power computations, output to Results Log, and return if error 
		iErr = osANOVA2Way_Power( iPower, iInteractions, dPowerAlpha, nPts, strSampleSizes,
			 &arANOVA2_Table[0] );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	}

	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function to read in and pre-process all required input datasets.
	Example:
		See the function osANOVA2Way above for a sample call.
	Parameter:
		iSpecifyLevelsBy=Radio button setting that determines how levels are specified: either by datasets (1)
			or by classification variables (2)
		nData=Number of input datasets
		vDataNames=Output vector containing names of all input datasets and their levels or variable types
		vFACTOR_A=Output vector containing integerized Factor A level indices into vLEVELSA for each data point
		vLEVELSA=Output vector containing Factor A level names indexed by vFACTOR_A
		nLevelsA=The output number of Factor A levels
		vFACTOR_B=Output vector containing integerized Factor B level indices into vLEVELSB for each data point
		vLEVELSB=Output vector containing Factor B level names indexed by vFACTOR_B
		nLevelsB=The output number of Factor B levels
		vDEP_VAR=Vector containing the dependent variable data whose levels are indicated by the paired vectors
			vFACTOR_A and vFACTOR_B
		iSize=The number of data values in the dependent variable vector
	Return:
		Returns required datasets and OSTAT_NO_ERROR on successful exit or an OSTAT error code on failure.
*/
int osANOVA2Way_Get_Data( int iSpecifyLevelsBy, int nData, vector<string> &vDataNames, vector<int> &vFACTOR_A,
	 vector<string> &vLEVELSA, int &nLevelsA, vector<int> &vFACTOR_B, vector<string> &vLEVELSB, int &nLevelsB,
	 vector<double> &vDEP_VAR, int &iSize )
{
	BOOL bErr;
	int ii, jj, iErr;
	string str, strDB, strErrMsg;
	char szTemp[40];

	// Matrix to hold the number of data values in each treatment level combination 
	matrix<int> mValuesPerCell;

	// Input dataset
	Dataset dsDATA_IN();
	
	if( iSpecifyLevelsBy == 1 ) // If levels are specified by Dataset...
	{
		int iiA, iiB;
		int iNewSize, iIncrSizeBy;
		string strDataName, strLevelA, strLevelB;
				
		// Set vector of strings to size 0 and then start adding levels datasets
		vLEVELSA.SetSize(0);
		vLEVELSB.SetSize(0);
		
		// Get all dataset and level names from list control in Two-Way ANOVA DB
		for( ii = 0; ii < nData; ii++ )
		{
			// Get name of dataset in row ii and column 1 of list control
			strDB.Format( "%%A=ANOVA2Way!SelectedDataLCNTRL.V%d_1$", ii + 1 );
			LT_execute( strDB );
			LT_get_str( "%A", szTemp, 40 );
			strDataName = szTemp;
			
			// Get name of Level A in row ii and column 2 of list control
			strDB.Format( "%%A=ANOVA2Way!SelectedDataLCNTRL.V%d_2$", ii + 1 );
			LT_execute( strDB );
			LT_get_str( "%A", szTemp, 40 );
			strLevelA = szTemp;
			
			// Get name of Level B in row ii and column 3 of list control
			strDB.Format( "%%A=ANOVA2Way!SelectedDataLCNTRL.V%d_3$", ii + 1 );
			LT_execute( strDB );
			LT_get_str( "%A", szTemp, 40 );
			strLevelB = szTemp;
			
			// Save dataset and level names for later output
			str.Format( OSTAT_STR_TOKEN, OSTAT_SEPARATOR_CHAR, strLevelA );
			vDataNames[ii] = strDataName + str;
			str.Format( OSTAT_STR_TOKEN, OSTAT_SEPARATOR_CHAR, strLevelB );
			vDataNames[ii] += str;
			
			// Add new level names to vector of strings for sorting
			osANOVA2Way_Add_String_to_Vector( strLevelA, vLEVELSA );
			osANOVA2Way_Add_String_to_Vector( strLevelB, vLEVELSB );
		}

		vLEVELSA.Sort(); // Sort level names in Factor A Levels vector (in ascending order) 
		vLEVELSB.Sort(); // Sort level names in Factor B Levels vector (in ascending order)
		
		// Read in dependent variable dataset and assign indices for level names in sorted vectors
		iSize = 0;  
		for( ii = 0; ii < nData; ii++ )
		{
			// Get dataset and level names 
			str = vDataNames[ii]; // Get current element of vDataNames string array
			strDataName = str.GetToken( 0, OSTAT_SEPARATOR_CHAR );
			strLevelA = str.GetToken( 1, OSTAT_SEPARATOR_CHAR );
			strLevelB = str.GetToken( 2, OSTAT_SEPARATOR_CHAR );

			// Read in dependent variable dataset values
			dsDATA_IN.Attach( strDataName );
			vector<double> vDATA_IN( dsDATA_IN, TRUE ); // Copy dataset to vector removing missing and masked values

			// Get index of level name from sorted vector of strings holding all level names
			iiA = osANOVA2Way_Add_String_to_Vector( strLevelA, vLEVELSA ) + 1; // Add 1 because NAG indexs from 1
			iiB = osANOVA2Way_Add_String_to_Vector( strLevelB, vLEVELSB ) + 1;
			
			iIncrSizeBy = vDATA_IN.GetSize(); // Get number of elements to be added
			iNewSize = iSize + iIncrSizeBy;   // Compute new size of variables
			vDEP_VAR.SetSize( iNewSize );     // Set new size of vectors
			vFACTOR_A.SetSize( iNewSize );
			vFACTOR_B.SetSize( iNewSize );

			for( jj = 0; jj < iIncrSizeBy; jj++ )       // Loop for each element in input dataset
			{
				vDEP_VAR[ iSize + jj ] = vDATA_IN[jj];  // Copy dependent variable value to vector
				vFACTOR_A[ iSize + jj ] = iiA;          // Assign index to level name in vLEVELSA
				vFACTOR_B[ iSize + jj ] = iiB;          // Assign index to level name in vLEVELSB
			}
			
			iSize = iNewSize; // Update new current size of vectors 	
			
			dsDATA_IN.Detach(); // No longer need input dataset
		}
	}
	else // Else levels specified by Classification Variables...
	{
		string strVarName, strVarType, strValue;
		string strDepVar, strFactAVar, strFactBVar;
		CategoricalData cdDATA_IN; // Categorical Dataset
		
		for( ii = 1; ii <= nData; ii++ )
		{
			// Get name of dataset in row ii and column 1 of list control
			strDB.Format( "%%A=ANOVA2Way!SelectedDataLCNTRL.V%d_1$", ii );
			LT_execute( strDB );
			LT_get_str( "%A", szTemp, 40 );
			strVarName = szTemp;

			// Get type of variable in row ii and column 2 of list control
			strDB.Format( "%%A=ANOVA2Way!SelectedDataLCNTRL.V%d_2$", ii );
			LT_execute( strDB );
			LT_get_str( "%A", szTemp, 40 );
			strVarType = szTemp;

			// Save dataset and variable type names for output
			str.Format( OSTAT_STR_TOKEN, OSTAT_SEPARATOR_CHAR, strVarType );
			vDataNames[ ii - 1 ] = strVarName + str;

			// Associate dataset name with variable type
			if( strVarType.Compare( AN2_DEPENDENT_VAR ) == 0)
				strDepVar = strVarName; // Dataset is the dependent variable
			else
				if( strVarType.Compare( AN2_FACTOR_A_VAR ) == 0 )
					strFactAVar = strVarName; // Dataset is the Factor A classification variable
				else
					strFactBVar = strVarName; // Dataset is the Factor B classification variable
		}

		// Read in Dependent Variable and get number of data points
		dsDATA_IN.Attach( strDepVar );
		
		// Do not allow missing or masked values because the dependent variable is paired row wise with classification variables
		if( DatasetHasNonNumericValues( dsDATA_IN ) ) 
		{
			strErrMsg.Format( AN2_DEPVAR_HAS_NONNUMERICS, strDepVar ); 
			Type_ErrorMsg( strErrMsg );
			return OSTAT_ABORT_NO_ERROR_MSG;
		}
		
		iSize = dsDATA_IN.GetSize();
		vDEP_VAR.SetSize( iSize );
		vDEP_VAR = dsDATA_IN;
		dsDATA_IN.Detach();
		
		// Read in Factor A dataset
		bErr = cdDATA_IN.Attach( strFactAVar, CMT_NOMINAL ); // Use Categorical Data
		if( !bErr )
			return OSTAT_ABORT_NO_ERROR_MSG;
		iSize = cdDATA_IN.GetSize();
		vFACTOR_A.SetSize( iSize );
		vFACTOR_A = cdDATA_IN;
		vLEVELSA = (StringArray) cdDATA_IN.Map; // Get Categorical Data Map holding Factor A level names
		cdDATA_IN.Detach();
		
		// Read in Factor B dataset
		bErr = cdDATA_IN.Attach( strFactBVar, CMT_NOMINAL ); // Use Categorical Data
		if( !bErr )
			return OSTAT_ABORT_NO_ERROR_MSG;
		iSize = cdDATA_IN.GetSize();
		vFACTOR_B.SetSize( iSize );
		vFACTOR_B = cdDATA_IN;
		vLEVELSB = (StringArray) cdDATA_IN.Map; // Get Categorical Data Map holding Factor B level names
		cdDATA_IN.Detach();
	}

	nLevelsA = vLEVELSA.GetSize(); // Get number of levels for Factor A
	nLevelsB = vLEVELSB.GetSize(); // Get number of levels for Factor B
	iSize = vDEP_VAR.GetSize(); // Get final size of variables
	
	// Count number of data values in each treatment combination group (Cell)
	mValuesPerCell.SetSize( nLevelsA, nLevelsB ); 
	iErr = osANOVA2Way_Count_Values_Per_Cell( vFACTOR_A, vFACTOR_B, mValuesPerCell, AN2_MIN_VALUES_PER_CELL, ii, jj );

	//  Terminate with error message if too few data values in treatment combination group (Cell)
	if( iErr != OSTAT_NO_ERROR )
	{
		strErrMsg.Format( AN2_TOO_FEW_DATA_PTS_IN_CELL, vLEVELSA[ii], vLEVELSB[jj], AN2_MIN_VALUES_PER_CELL ); 
		Type_ErrorMsg( strErrMsg );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
			
	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function to count the number of data values in each treatment level combination. The counts are
		stored in a matrix because there are two dimensions in a Two-Way ANOVA.
	Example:
		See the function osANOVA2Way_Get_Data above for a sample call.
	Parameter:
		v1=Input vector containing indices to Factor A levels identifying the first dimension of each cell address in matrix 
		v2=Input vector containing indices to Factor B levels identifying the second dimension of each cell address in matrix 
		mCount=Number of data points in each cell of matrix
		iThreshold=Minimum number of data points required in each cell of matrix
		iRow=Row index of first cell in matrix having fewer than iThreshold data points. Returns -1 if no such cell is found. 
		iCol=Column index of first cell in matrix having fewer than iThreshold data points. Returns -1 if no such cell is found.
	Return:
		Returns the matrix of cell counts, the index of the first cell having fewer than iThreshold data points, and the
		number of cells having fewer than iThreshold data points or zero if no such cells are found.
*/
int	osANOVA2Way_Count_Values_Per_Cell( vector<int> &v1, vector<int> &v2, matrix<int> &mCount, int iThreshold, int &iRow, int &iCol )
{
	int ii, jj, iRet;
	int n1, n2, nPts; 
	
	// Vectors should have same size
	nPts = v1.GetSize();
	if( nPts > v2.GetSize() )
		nPts = v2.GetSize();

	// Get the number of rows and columns in the matrix which is the same as the number of unique indices in each vector (v1 and v2)
	n1 = mCount.GetNumRows();
	n2 = mCount.GetNumCols();

	// Initialize count matrix 
	mCount = 0;
	
	// Initialize cell index and count of cells having fewer than iThreshold data points 
	iRet = OSTAT_NO_ERROR;
	iRow = -1;
	iCol = -1;

	// Loop through all elements in vectors counting number
	for( ii = 0; ii < nPts; ii++ )
	{
		mCount[ v1[ii] - 1 ][ v2[ii] - 1 ]++; // Increment cell count...levels are indexed from 1 whereas matices are indexed from 0
		                                      //  so subtract 1 from indices stored in vectors
	}

	// Loop through all cells in matrix looking for cells where count is less than iThreshold
	for( ii = 0; ii < n1; ii++ ) // For all ii rows in matrix...
	{
		for( jj = 0; jj < n2; jj++ ) // For all jj columns in matrix...
		{
			if( mCount[ii][jj] < iThreshold ) // If cell count is lower than threshold...
			{
				if( iRet == OSTAT_NO_ERROR ) // If first cell lower than iThreshold...
				{
					iRow = ii; // Set return address of cell
					iCol = jj;
				} // End first cell
				iRet++; // Count number of cells less than iThreshold
			} // End cell count lower than iThreshold
		} // End all jj columns
	} // End all ii rows  
	
	return iRet;
}

/**
		Two-Way ANOVA function to add a string to a vector if the string is not already in the vector. The index
		of the string in the vector is returned whether the string is found or added.
	Example:
		See the function osANOVA2Way_Get_Data above for a sample call.
	Parameter:
		strIn=String to search for and add if not found
		vSTRINGS=Vector of strings to search
	Return:
		Returns the 0 based index of the string in the vector whether found or added.
*/
int osANOVA2Way_Add_String_to_Vector( string strIn, vector<string> &vSTRINGS )
{
	int ii, iSize;
	
	iSize = vSTRINGS.GetSize();

	// Loop on all strings in vector
	for( ii = 0; ii < iSize; ii++ )
	{
		if( strIn.Compare( vSTRINGS[ii] ) == 0 ) // If string is in vector break loop when ii < iSize
			break;			
	}

	// If string not in vector then add it	
	if( ii == iSize )
	{
		vSTRINGS.SetSize( iSize + 1 );
		vSTRINGS[ii] = strIn;
	}
	
	return ii; // Return 0 based index of string in vector
}

/**
		Two-Way ANOVA function used (at one point) to count the number of unique data values (Levels) in a dataset.
	Example:
		This function is not currently used by OSTAT but is left in until it can be moved to App_Utils.h for general use.
	Parameter:
		nPts=Number of data points in dataset
		dsDATASET=Dataset whose unique values are counted
		nCount=Output the number of unique data values in a data set
	Return:
		Returns the number of unique data values in a data set.
*/
int osANOVA2Way_Count_Unique_Values( int nPts, Dataset &dsDATASET, int &nCount ) 
{
	int ii;
	BOOL bErr; 
	
	// Make temporary copy of passed dataset
	Dataset dsSortedDATA( dsDATASET );
	
	// Sort temporary copy of dataset 
	bErr = Data_sort( &dsSortedDATA );
	if( bErr == FALSE )
		return OSTAT_DATA_SORT_ERROR2;

	// Loop through dataset counting the number of unique values in the dataset
	nCount = 1;
	for( ii = 0; ii < nPts - 1; ii++ )
	{
		if( dsSortedDATA[ii] != dsSortedDATA[ii + 1] ) // If current value does not equal next value then...
			nCount++;                                  //  increment count of unique vales in dataset
	}		

	dsSortedDATA.Detach();

	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function to compute the Two-Way ANOVA F table. The Origin Two-Way ANOVA dialog
		box uses a linear regression approach in order to support unequal cell sizes (However, empty
		cells are not supported). The reference for Origin's two-way analysis of variance is Applied
		Linear Statistical Models, Neter J., Kutner M., Nachtsheim C., and Wasserman W. (1996), The
		McGraw-Hill Companies, Inc., Boston, MA. See Section 22.2 for a discussion of how to use dummy
		variables for testing both factor main effects and interaction effects. The Origin Two-Way ANOVA
		dialog box also makes use of the NAG functions nag_dummy_vars (g04eac) to create the necessary
		design matrices and nag_regsn_mult_linear (g02dac) to perform the linear regressions of the
		design matrices. The results of the linear regressions are then used to construct the two-way
		ANOVA table.
	Example:
		See the main Two-Way ANOVA function osANOVA2Way above for a sample call.
	Parameters:
		iInteractions=Flag indicating whether (1) or not (0) interaction effects are to be determined
		vFACTOR_A=Vector containing integerized level indices (1 based) for Factor A
		nLevelsA=The number of Factor A levels
		vFACTOR_B=Vector containing integerized level indices (1 based) for Factor B
		nLevelsB=The number of Factor D levels
		vDEP_VAR=Vector containing dependent variable data values
		nPts=Number of data values
		arANOVA2_Table=A four element array of an ANOVA2_Row structure holding all computed F Table
			values one element for each row of the ANOVA F table (A, B, A * B, and Error).
	Return:
		Returns a four element array of an ANOVA2_Row structure holding all computed F Table values
		which has one element for each row in the ANOVA F table (A, B, A * B, and Error). Also returns
		OSTAT_NO_ERROR on successful exit or an OSTAT error code on failure.
*/
int osANOVA2Way_Compute_ANOVA_Table( int iInteractions, vector<int> &vFACTOR_A, int nLevelsA, vector<int> &vFACTOR_B,
	 int nLevelsB, vector<double> &vDEP_VAR, int nPts, ANOVA2_Row *arANOVA2_Table )
{
	int ii, iErr, iAxBDim, iXDim;
	double dRss, dDf;
	
	int ariLinRegrErr[4]; // Array to hold ANOVA Linear regression errors
	int ariProbFErr[4];   // Array to hold ANOVA Prob F errors

	NagError neErr;
	NagError *pneFail = &neErr; // NAG error structure
		
	// Compute dummy variables for Factor A for use in linear regressions
	matrix<double> mX_FACT_A;
	mX_FACT_A.SetSize( nPts, nLevelsA - 1 );
	iErr = osANOVA2Way_Compute_Dummy_Vars( nPts, nLevelsA, vFACTOR_A, mX_FACT_A );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;	
	
	// Compute dummy variables for Factor B for use in linear regressions
	matrix<double> mX_FACT_B;
	mX_FACT_B.SetSize( nPts, nLevelsB - 1 );
	iErr = osANOVA2Way_Compute_Dummy_Vars( nPts, nLevelsB, vFACTOR_B, mX_FACT_B );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;
		
	matrix<double> mX_FACTs_AxB;
	// Compute dummy variables for Factors A x B for use in linear regressions
	if( iInteractions )
	{
		iAxBDim = ( nLevelsA - 1 ) * ( nLevelsB - 1 );
		mX_FACTs_AxB.SetSize( nPts, iAxBDim );
		iErr = osANOVA2Way_Compute_FACTsAxB_Dummy_Vars( nPts, nLevelsA, mX_FACT_A, nLevelsB, mX_FACT_B, mX_FACTs_AxB );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	}
	else
		iAxBDim = 0;

	matrix<double> mX; // Main dummy variable matrix
	
	// Element 0 is A source, element 1 is B source, element 2 is A * B source (interactions),
	//  and element 3 is Error source 	

	// *** Perform linear regression with all dummy variables computing the residual sum ***
	// *** of squares (dRss) and degrees of freedom (dDf) for the Error source (compute  ***
	// *** first as this is needed when computing other sources)                         *** 

	// Combine Factor A, Factor B, and (if needed) Interaction dummy variable matrices
	iXDim = nLevelsA + nLevelsB + iAxBDim - 2;
	mX.SetSize( nPts, iXDim );
	osANOVA2Way_Copy_Matrix( nPts, nLevelsA - 1, mX_FACT_A, 0, mX ); // Copy Factor A dummy variables
	osANOVA2Way_Copy_Matrix( nPts, nLevelsB - 1, mX_FACT_B, nLevelsA - 1, mX ); // Copy Factor B dummy variables
	if( iInteractions ) // If Interactions are to be computed...
		osANOVA2Way_Copy_Matrix( nPts, iAxBDim, mX_FACTs_AxB, nLevelsA + nLevelsB - 2, mX ); // Copy Interaction A * B dummy variables
	
	// Perform linear regression for Error row
	iErr = osANOVA2Way_Perform_Linear_Regression( nPts, iXDim, mX, vDEP_VAR, dRss, dDf );
	ariLinRegrErr[3] = iErr;

	// Compute Error row of Two-Way ANOVA table
	arANOVA2_Table[3].dDF = dDf;
	arANOVA2_Table[3].dSS = dRss;
	arANOVA2_Table[3].dMS = arANOVA2_Table[3].dSS / arANOVA2_Table[3].dDF;
	arANOVA2_Table[3].dF = NANUM;
	arANOVA2_Table[3].dP = NANUM;
	ariProbFErr[3] = NE_NOERROR;
	
	// *** Perform linear regression with all but factor A dummy variables computing the residual sum ***
	// *** of squares (dRss) and degrees of freedom (dDf) for the A source                            ***

	// Combine Factor B and (if needed) Interaction dummy variable matrices	
	iXDim = nLevelsB + iAxBDim - 1;
	mX.SetSize( nPts, iXDim );
	osANOVA2Way_Copy_Matrix( nPts, nLevelsB - 1, mX_FACT_B, 0, mX ); // Copy Factor B dummy variables 
	if( iInteractions ) // If Interactions are to be computed...
		osANOVA2Way_Copy_Matrix( nPts, iAxBDim, mX_FACTs_AxB, nLevelsB - 1, mX ); // Copy Interaction A * B dummy variables

	// Perform linear regression for A row	
	iErr = osANOVA2Way_Perform_Linear_Regression( nPts, iXDim, mX, vDEP_VAR, dRss, dDf );
	ariLinRegrErr[0] = iErr;

	// Compute row A of Two-Way ANOVA table
	arANOVA2_Table[0].dDF = dDf - arANOVA2_Table[3].dDF;
	arANOVA2_Table[0].dSS = dRss - arANOVA2_Table[3].dSS;
	arANOVA2_Table[0].dMS = arANOVA2_Table[0].dSS / arANOVA2_Table[0].dDF;
	arANOVA2_Table[0].dF = arANOVA2_Table[0].dMS / arANOVA2_Table[3].dMS;
	arANOVA2_Table[0].dP = 1 - nag_prob_f_dist( Nag_LowerTail, arANOVA2_Table[0].dF, arANOVA2_Table[0].dDF,
		 arANOVA2_Table[3].dDF, pneFail );
	ariProbFErr[0] = pneFail->code;

	// *** Perform linear regression with all but factor B dummy variables computing the residual sum ***
	// *** of squares (dRss) and degrees of freedom (dDf) for the B source                            ***

	// Combine Factor A and (if needed) Interaction dummy variable matrices		
	iXDim = nLevelsA + iAxBDim - 1;
	mX.SetSize( nPts, iXDim );
	osANOVA2Way_Copy_Matrix( nPts, nLevelsA - 1, mX_FACT_A, 0, mX ); // Copy Factor A dummy variables 
	if( iInteractions ) // If Interactions are to be computed...
		osANOVA2Way_Copy_Matrix( nPts, iAxBDim, mX_FACTs_AxB, nLevelsA - 1, mX ); // Copy Interaction A * B dummy variables

	// Perform linear regression for B row
	iErr = osANOVA2Way_Perform_Linear_Regression( nPts, iXDim, mX, vDEP_VAR, dRss, dDf );
	ariLinRegrErr[1] = iErr;

	// Compute row B of Two-Way ANOVA table
	arANOVA2_Table[1].dDF = dDf - arANOVA2_Table[3].dDF;
	arANOVA2_Table[1].dSS = dRss - arANOVA2_Table[3].dSS;
	arANOVA2_Table[1].dMS = arANOVA2_Table[1].dSS / arANOVA2_Table[1].dDF;
	arANOVA2_Table[1].dF = arANOVA2_Table[1].dMS / arANOVA2_Table[3].dMS;
	arANOVA2_Table[1].dP = 1 - nag_prob_f_dist( Nag_LowerTail, arANOVA2_Table[1].dF, arANOVA2_Table[1].dDF,
		 arANOVA2_Table[3].dDF, pneFail );
	ariProbFErr[1] = pneFail->code;
	
	// *** If Interactions are to be computed perform linear regression with all but cross (AxB) dummy ***
	// *** variables computing the residual sum of squares (dRss) and degrees of freedom (dDf) for the ***
	// *** A * B source                                                                                ***
	
	if( iInteractions ) // If Interactions are to be computed...
	{
		iXDim = nLevelsA + nLevelsB - 2;
		mX.SetSize( nPts, iXDim );
		osANOVA2Way_Copy_Matrix( nPts, nLevelsA - 1, mX_FACT_A, 0, mX ); // Copy Factor A dummy variables 
		osANOVA2Way_Copy_Matrix( nPts, nLevelsB - 1, mX_FACT_B, nLevelsA - 1, mX ); // Copy Factor B dummy variables 
		
		// Perform linear regression for A * B row
		iErr = osANOVA2Way_Perform_Linear_Regression( nPts, iXDim, mX, vDEP_VAR, dRss, dDf );
		ariLinRegrErr[2] = iErr;

		// Compute A * B row of Two-Way ANOVA table
		arANOVA2_Table[2].dDF = dDf - arANOVA2_Table[3].dDF;
		arANOVA2_Table[2].dSS = dRss - arANOVA2_Table[3].dSS;
		arANOVA2_Table[2].dMS = arANOVA2_Table[2].dSS / arANOVA2_Table[2].dDF;
		arANOVA2_Table[2].dF = arANOVA2_Table[2].dMS / arANOVA2_Table[3].dMS;
		arANOVA2_Table[2].dP = 1 - nag_prob_f_dist( Nag_LowerTail, arANOVA2_Table[2].dF, arANOVA2_Table[2].dDF,
			 arANOVA2_Table[3].dDF, pneFail );
		ariProbFErr[2] = pneFail->code;
	}

	// *** Finish regressions for all sources first (above code) and only then return error code if needed ***
	// *** because each regression may provide information about where problem exists. ii==0 is A source,  ***
	// *** ii==1 is B source, ii==2 is A * B source (Interactions), ii==3 is Error source                  ***
	
	for( ii = 0; ii < 4; ii++ ) // Loop checking on error codes of all four regressions
	{
		if( ii != 2 || iInteractions ) // If not Interactions row or Interactions are computed (and ii==2) check error codes
		{
			if( ariLinRegrErr[ii] != OSTAT_NO_ERROR ) // Check for linear regression error
				return OSTAT_ANOVA2_LIN_REGR_ERROR + ii;
			if( ariProbFErr[ii] != NE_NOERROR )       // Check for NAG Prob F error
				return OSTAT_ANOVA2_PROB_F_ERROR + ii;
			if( arANOVA2_Table[ii].dDF < 1 )          // Check for DoF error to prevent division by zero and illogical F-table lookup
				return OSTAT_ANOVA2_DOF_ERROR + ii;
			if( ii < 3 ) // F and P are not computed for Error term (ii==3) so only check F and P for errors if ii < 3
			{
				if( arANOVA2_Table[ii].dF == NANUM )  // Check for F Value definition error
					return OSTAT_ANOVA2_FVALUE_ERROR + ii; 
				if( arANOVA2_Table[ii].dP == NANUM )  // Check for P Value definition error
					return OSTAT_ANOVA2_PVALUE_ERROR + ii;
			}
		}
	}
	return OSTAT_NO_ERROR; // Otherwise return with no errors

}

/**
		Two-Way ANOVA function to compute dummy variables for use with linear regression function.
	Example:
		See the Two-Way ANOVA function osANOVA2Way_Compute_ANOVA_Table above for a sample call.
	Parameters:
		nPts=Number of data points
		nLevels=Number of levels
		vFACT=Vector containing integerized level indices (1 based) for Factor
		mX=Output matrix containing computed dummy variables
	Return:
		Returns a matrix containing dummy variables for use with a linear regression function. Also
		returns OSTAT_NO_ERROR on successful exit or an OSTAT error code on failure.
*/
int osANOVA2Way_Compute_Dummy_Vars( int nPts, int nLevels, vector<int> &vFACT, matrix<double> &mX )
{
	int ii, jj, iErr, nTdx;

	// 2nd dimension of matrix is always one less than nLevels 
	nTdx = nLevels - 1;
	
	// NAG requires vector of size 1 for Nag_LastLevel
	vector<double> vV;
	vV.SetSize( 1 );
	
	// Output by NAG vector but not used, contains number of replications for each level of factor 
	vector<double> vNumReps;
	vNumReps.SetSize( nLevels );
	
	// From <NAG\OCN_g04.h>: g04eac nag_dummy_vars: Compute NAG dummy variables
	iErr = nag_dummy_vars( Nag_LastLevel, nPts, nLevels, vFACT, mX, nTdx, vV, vNumReps );	
	if( iErr != NE_NOERROR )
		return OSTAT_NAG_DUMMY_VARS_ERROR;

	// Modify NAG's dummy variables by replacing any row of all 0's with row of all -1's
	for( ii = 0; ii < nPts; ii++ )				// Loop on all rows of matrix 
	{
		for( jj = 0; jj < nTdx; jj++ )			// Loop on all columns of matrix
		{
			if( mX[ii][jj] != 0 )				// If element of row not equal to 0 break out of loop 
				break;							//   so that jj not equal to nLevels when inner loop ends
		}
		if( jj == nTdx )						// If no break from inner loop then all elements of row must be 0
		{
			for( jj = 0; jj < nTdx; jj++ )		// So loop again on all columns of matrix
			{
				mX[ii][jj] = -1;				//   and set each element of row to -1
			}
		}
	}
	
	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function to compute dummy variables for Factor A * Factor B for use with linear regression
		function.
	Example:
		See the Two-Way ANOVA function osANOVA2Way_Compute_ANOVA_Table above for a sample call.
	Parameters:
		nPts=Number of data points
		nLevelsA=Number of levels in Factor A
		mX_FACT_A=Input matrix of Factor A dummy variables
		nLevelsB=Number of levels in Factor B
		mX_FACT_B=Input matrix of Factor B dummy variables
		mX_FACTs_AxB=Output matrix containing computed dummy variables for Factor A * Factor B
	Return:
		Returns a matrix containing dummy variables for use with a linear regression function. Also
		returns OSTAT_NO_ERROR on successful exit.
*/
int osANOVA2Way_Compute_FACTsAxB_Dummy_Vars( int nPts, int nLevelsA, matrix<int> &mX_FACT_A, int nLevelsB,
	 matrix<int> &mX_FACT_B, matrix<int> &mX_FACTs_AxB )
{
	// *** This function performs vector multiplication of every column in mX_FACT_A times every ***
	// *** column in mX_FACT_B and stores the results in mX_FACTs_AxB. This column times column  ***
	// *** form of matrix multiplication is different than the traditional matrix multiplication ***
	// *** where columns are multiplied by rows.                                                 ***
	
	int jja, jjb, jjab, ii; // Loop indices

	jjab = 0;
	for( jja = 0; jja < nLevelsA - 1; jja++ )		// Loop on all columns of matrix mX_FACT_A 
	{
		for( jjb = 0; jjb < nLevelsB - 1; jjb++ )	// Loop on all columns of matrix mX_FACT_B
		{
			for( ii = 0; ii < nPts; ii++ )			// Loop on all rows
			{
				mX_FACTs_AxB[ii][jjab] = mX_FACT_A[ii][jja] * mX_FACT_B[ii][jjb]; // Multiply cell values			
			}
			jjab++; // Increment column index of output matrix mX_FACTs_AxB
		}
	}
	
	return OSTAT_NO_ERROR;	
}

/**
		Two-Way ANOVA function to copy from a smaller matrix into part of a second larger matrix.  The resulting
		larger martix is then used in the linear regressions of the Two-Way ANOVA.
	Example:
		See the Two-Way ANOVA function osANOVA2Way_Compute_ANOVA_Table above for a sample call.
	Parameters:
		nPts=Number of data points
		nCols=Number of columns in matrix being copied from
		mXfrom=Matrix being copied from
		nStartCol=Column number in larger matrix being copied into where the copying is to start (indexed from 0)
		mXinto=Matrix being copied into
	Return:
		Returns a matrix containing dummy variables for use with a linear regression function. Also
		returns OSTAT_NO_ERROR on successful exit.
*/
int osANOVA2Way_Copy_Matrix( int nPts, int nCols, matrix<double> &mXfrom, int nStartCol, matrix<double> &mXinto )
{
	int ii, jj; 
	
	for( jj = 0; jj < nCols; jj++ ) // Loop on all columns in smaller copy from matrix
	{
		for( ii = 0; ii < nPts; ii++ ) // Loop on all rows 
		{
			mXinto[ii][nStartCol + jj] = mXfrom[ii][jj]; // Copy cell from samller from matrix to larger into matrix with column offset by nStartCol
		}
	}
	
	return OSTAT_NO_ERROR; 
}

/**
		Two-Way ANOVA function to perform the linear regression on the dummy variables matrix. The results of
		the regression are used to compute the elements of the Two-Way ANOVA table.
	Example:
		See the Two-Way ANOVA function osANOVA2Way_Compute_ANOVA_Table above for a sample call.
	Parameters:
		nPts=Number of rows or data points in the dummy variables matrix
		nTdx=Number of columns or dummy variables in the dummy variables matrix
		mX=Matrix containing the input dummy variables for use in the linear regression
		vY=The Dependent Variable of the Two-Way ANOVA
		dRss=The output residual sum of squares for the regression...used in the ANOVA F table
		dDf=The output degrees of freedom associated with the residual sum of squares...used in the ANOVA F table
	Return:
		Returns the residual sum of squares and the associated degrees of freedom for the linear regression
		both of which are used in the ANOVA F table. Also returns OSTAT_NO_ERROR on successful exit or
		an OSTAT error code on failure.
*/
int osANOVA2Way_Perform_Linear_Regression( int nPts, int nTdx, matrix<double> &mX, vector<double> &vY,
	 double &dRss, double &dDf )
{
 	int ii, iErr;

	int nM = nTdx; // The second dimension of the matrix mX equals the number of independent (dummy) variables

	// Indicates which of the potential independent variables are to be included in the model...use all so set to 1
	vector<int> vSX;	
	vSX.SetSize( nM );
	for( ii = 0; ii < nM; ii++ )
	{
		vSX[ii] = 1;
	}
	
	// *** The following arguments are not really used by Origin Two-Way ANOVA but NAG requires them ***
	// The number P of independent variables in the model including the mean or intercept if present	
	int iP = nTdx + 1;
	
	// The least-squares estimates of the parameters of the regression model
	vector<double> vB;
	vB.SetSize( iP );
	
	// The standard errors of the iP parameter estimates given in B
	vector<double> vSE;
	vSE.SetSize( iP );
	
	// The variance-covariance matrix of estimated parameters in B
	vector<double> vCOV;
	vCOV.SetSize( iP * ( iP + 1 ) / 2);
	
	// The (weighted) residuals
	vector<double> vRES;
	vRES.SetSize( nPts );
	
	// The diagonal elements of H
	vector<double> vH;
	vH.SetSize( nPts );

	// The second dimension of the array Q
	int nTdq;
	nTdq = iP + 1;

	// The results of the QR decomposition
	matrix<double> mQ;
	mQ.SetSize( nPts, nTdq );
	
	// TRUE if a singular value decomposition has been performed, otherwise FALSE
	BOOL bSvd;

	// The rank of the independent variables
	int iRank;

	// Details of the QR decomposition and SVD if used
	vector<double> vP;
	vP.SetSize( 2 * iP + iP * iP );
	
	// Information which is needed by nag_regsn_mult_linear_newyvar_if svd = TRUE
	vector<double> vCOMAR;
	vCOMAR.SetSize( 5 * ( iP - 1 ) + iP * iP );
	// *** The above arguments are not used by Origin Two-Way ANOVA but NAG requires them ***
	
	// From <NAG\OCN_g02.h>: g02dac nag_regsn_mult_linear: Perform regression of dummy variables matrix. Do not use 
	// weights so pass NULL, Tol=AN2_LINEAR_REGRESS_TOLERANCE is used to decide the rank of the independent variables.
	iErr = nag_regsn_mult_linear( Nag_MeanInclude, nPts, mX, nTdx, nM, vSX, iP, vY, NULL, &dRss, &dDf,
		 vB, vSE, vCOV, vRES, vH, mQ, nTdq, &bSvd, &iRank, vP, AN2_LINEAR_REGRESS_TOLERANCE, vCOMAR );
	if( iErr != NE_NOERROR )
		return iErr;
	
	return OSTAT_NO_ERROR;	
}

/**
		Two-Way ANOVA function to output Two-Way ANOVA table to Results Log.
	Example:
		See function osANOVA2Way above for sample call.
	Parameters:
		iSpecifyLevelsBy=Method of specifying levels. By Dataset=1, by Classification Variable=2
		nData=Number of selected datasets
		vDataNames=Vector containing all selected dataset and level names
		iInteractions=Flag indicating whether (1) or not (0) to include interactions in Two-Way ANOVA results
		arANOVA2_Table=Pointer to a 4 element array of an ANOVA2_Row structure holding results of Two-Way ANOVA
	Return:
		Outputs Two-Way ANOVA table to Results Log. Also returns OSTAT_NO_ERROR on successful exit or
		an OSTAT error code on failure.
*/
int osANOVA2Way_ANOVA_Table_Output_to_ResultsLog( int iSpecifyLevelsBy, int nData, vector<string> &vDataNames,
	 int iInteractions, ANOVA2_Row *arANOVA2_Table )
{
	int ii, iRow, iCol, iDF;
	Worksheet wksANOVAData;
	Worksheet wksANOVATable;
	string strTablePath;
	string strTableName;

	string strElement, strDataName, strLevelName, strVarType;

	// *** Output Two-Way ANOVA selected dataset and levels/variable type names ***
	// Create and attach to a temporary Two-Way DataNames output worksheet
	strTablePath = GetFullPath( "osANOVA2Data.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksANOVAData.Open( strTablePath, CREATE_TEMP );
	strTableName = wksANOVAData.GetPage().GetName();
	
	// All rows and columns are indexed from 0
		
	// Insert a blank line in the DataNames table for each selected dataset in row 7 of worksheet
	iRow = 6;
	Type_Insert_Blank_Lines_in_Wks( wksANOVAData, iRow, nData );		
	
	// Output selected dataset and levels/variable type names starting in column 2 of worksheet.
	if( iSpecifyLevelsBy == 1 ) // If levels are specified by dataset...
	{
		// Output selected dataset names and levels starting in row 5 and column 2 of worksheet
		for( ii = 0; ii < nData; ii++, iRow++ )
		{
			strElement = vDataNames[ii]; // Get current element of vDataNames string array
			
			// Output selected dataset name in column 2
			iCol = 1;
			strDataName = strElement.GetToken( 0, OSTAT_SEPARATOR_CHAR );
			wksANOVAData.SetCell( iRow, iCol, strDataName );
		
			// Output Level A Name in column 3
			iCol++;
			strLevelName = strElement.GetToken( 1, OSTAT_SEPARATOR_CHAR );
			wksANOVAData.SetCell( iRow, iCol, strLevelName );
			
			// Output Level B Name in column 4
			iCol++;
			strLevelName = strElement.GetToken( 2, OSTAT_SEPARATOR_CHAR );
			wksANOVAData.SetCell( iRow, iCol, strLevelName );
		}
	} // End levels specified by dataset
	else // If levels specified by classification variables...
	{
		// Output "Variable Type" in row 3 and column 3
		iRow = 4;
		iCol = 2;
		wksANOVAData.SetCell( iRow, iCol, AN2_VAR_TYPE );
	
		// Blank out column 4 header
		iCol++;
		wksANOVAData.SetCell( iRow, iCol, APP_UTILS_BLANK_SPACE );

		// Output selected dataset names and variable types starting in row 5 and column 2 of worksheet
		for( ii = 0, iRow = 6; ii < nData; ii++, iRow++ )
		{
			strElement = vDataNames[ii]; // Get current element of string array
			
			// Output selected dataset name in column 2
			iCol = 1;
			strDataName = strElement.GetToken( 0, OSTAT_SEPARATOR_CHAR );
			wksANOVAData.SetCell( iRow, iCol, strDataName );
		
			// Out variable type in column 3
			iCol++;
			strVarType = strElement.GetToken( 1, OSTAT_SEPARATOR_CHAR );
			wksANOVAData.SetCell( iRow, iCol, strVarType );
		}
	} // End levels specified by classification variables
	
	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output ANOVA DataNames worksheet to Results Log
	wksANOVAData.Type( TYPETARGET_OUTPUTLOG, NULL, 5 );

	// *** Output Two-Way ANOVA Table ***		
	// Create and attach to a temporary Two-Way ANOVA Table output worksheet
	strTablePath = GetFullPath( "osANOVA2Table.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksANOVATable.Open( strTablePath, CREATE_TEMP );
	strTableName = wksANOVATable.GetPage().GetName();

	// Loop on four rows in ANOVA output table, Source: A(ii=0), B(ii=1), A * B(ii=2), and Error(ii=3). 
	iRow = 5; // Output of ANOVA values starts in row 6 (indexed from 0).
	for(ii = 0; ii < 4; iRow++, ii++)
	{
		// Output of ANOVA values starts in column 3 of worksheet (indexed from 0).
		iCol = 2; 
		iDF = (int) arANOVA2_Table[ii].dDF;
		wksANOVATable.SetCell( iRow, iCol, iDF ); // DoF in column 3
		
		iCol++;
		wksANOVATable.SetCell( iRow, iCol, arANOVA2_Table[ii].dSS ); // Sum of Squares in column 4
		
		iCol++;
		wksANOVATable.SetCell( iRow, iCol, arANOVA2_Table[ii].dMS ); // Mean Square in column 5
			
		if( ii < 3 ) // If not Error row output .dF and .dP
		{
			iCol++;
			wksANOVATable.SetCell( iRow, iCol, arANOVA2_Table[ii].dF ); // F Value in column 6
		
			iCol++;
			wksANOVATable.SetCell( iRow, iCol, arANOVA2_Table[ii].dP ); // Output P Value in column 7
		}	
	}
	
	if( !iInteractions )
		wksANOVATable.DeleteRow( iRow - 2 ); // If no Interactions delete Interactions row
	
	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output ANOVA Table worksheet to Results Log
	wksANOVATable.Type( TYPETARGET_OUTPUTLOG, NULL, 8 );

	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function to perform a Means Comparison analysis for a single factor of the
		Two-Way ANOVA dialog box.
	Example:
		See function osANOVA2Way above for a sample call.
	Parameters:
		cFactor=Factor 'A' or Factor 'B'
		iTest=Bitwise integer specifying which Means Comparison test(s) if any to run:
			Bonferroni=1, Scheffe=2, Tukey=4
		vFACTOR=Vector containing integerized level indices (1 based) into vLEVELS vector
		vLEVELS=Vector containing names of levels indexed by vFACTOR vector
		nLevels=Number of levels
		vDEP_VAR=Vector containing the dependent variable data whose levels are indicated by the paired
			vector vFACTOR
		nPts=Number of data points
		dDFE=Degrees of Freedom for the Error term of the ANOVA table
		dMSE=Mean Square for the Error term of the ANOVA table
		dSigLevel=Significance Level 
	Return:
		Computes and outputs Two-Way ANOVA Means Comparison results to Results Log. Also returns
		OSTAT_NO_ERROR on successful exit or an OSTAT error code on failure.
*/
int osANOVA2Way_Means_Comparison( char cFactor, int iTest, vector<int> &vFACTOR, vector<string> &vLEVELS,
	 int nLevels, vector<double> &vDEP_VAR, int nPts, double dDFE, double dMSE, double dSigLevel )
{
	int iErr, iSize;
	double dCLevel;
	string strTestName;

	// Vector to hold the number of data points in each level of Factor
	vector<int> vNPTS;
	vNPTS.SetSize( nLevels );

	// Vector to hold the mean of the dependent variable for each level of Factor	
	vector<double> vMEAN;
	vMEAN.SetSize( nLevels );
	
	// Compute the number of data points and the mean of the dependent variable for each level of Factor
	iErr = osANOVA2Way_Compute_MEANS_of_Levels( vFACTOR, nLevels, vDEP_VAR, nPts, vNPTS, vMEAN );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;

	// Vector to hold the standard errors of the differences of the means
	vector<double> vC;
	iSize = pow( nLevels, 2 );
	vC.SetSize( iSize );
	
	// Function computes the standard errors of the differences of the means
	iErr = osANOVA_Compute_StdErr_MeanDiffs( nLevels, dMSE, vNPTS, vC );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;

	// Compute confidence level from level of significance and size of CI vectors
	dCLevel = 1 - dSigLevel;
	iSize = nLevels * ( nLevels - 1 ) / 2;

	// Vector to hold the lower limit of the simultaneous confidence interval of the differences of the means
	vector<double> vCIL;
	vCIL.SetSize( iSize );

	// Vector to hold the upper limit of the simultaneous confidence interval of the differences of the means
	vector<double> vCIU;
	vCIU.SetSize( iSize );

	// Vector to hold Significance decisions: 1=YES and 0=NO
	vector<int> vISIG;
	vISIG.SetSize( iSize );
	
	// Perform Bonferroni test?
	if( iTest & 1 )
	{
		strTestName = AN_BONFERRONI_TEST;

		// From <NAG\OCN_g04.h>: g04dbc nag_anova_confid_interval: Perform Bonferroni test
		iErr = nag_anova_confid_interval( Nag_BonferroniInterval, nLevels, vMEAN, dDFE, vC, nLevels, dCLevel, vCIL,
			 vCIU, vISIG );
		if( iErr != NE_NOERROR )
			return OSTAT_CONF_INT_ERROR4;

		// Output the means comparison analysis for the Bonferroni test
		iErr = osANOVA2Way_Means_Comparison_Output_to_ResultsLog( cFactor, strTestName, nLevels, vLEVELS,
			 vMEAN, vCIL, vCIU, vISIG, dSigLevel );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	} // End Bonferroni		
	
	// Perform Scheffe' test?
	if( iTest & 2 )
	{
		strTestName = AN_SCHEFFE_TEST;

		// From <NAG\OCN_g04.h>: g04dbc nag_anova_confid_interval: Perform Scheffe' test
		iErr = nag_anova_confid_interval( Nag_ScheffeInterval, nLevels, vMEAN, dDFE, vC, nLevels, dCLevel, vCIL,
			 vCIU, vISIG );
		if( iErr != NE_NOERROR )
			return OSTAT_CONF_INT_ERROR5;
		
		// Output the means comparison analysis for the Scheffe' test
		iErr = osANOVA2Way_Means_Comparison_Output_to_ResultsLog( cFactor, strTestName, nLevels, vLEVELS,
			 vMEAN, vCIL, vCIU, vISIG, dSigLevel );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	} // End Scheffe
	
	// Perform Tukey test?
	if( iTest & 4 )
	{
		strTestName = AN_TUKEY_TEST;

		// SET_MAX_TUKEY_RESID_DF QA70-688: Limit max value of dDFE since NAG function nag_anova_confid_interval
		// crashes for Tukey test when dDFE > AN_MAX_TUKEY_RESID_DF. There is little difference in function values
		// when dDFE is large (>120) so set AN_MAX_TUKEY_RESID_DF as max value for dDFE 
		if( dDFE > AN_MAX_TUKEY_RESID_DF )
			dDFE = AN_MAX_TUKEY_RESID_DF;
		// END SET_MAX_TUKEY_RESID_DF QA70-688
		
		// From <NAG\OCN_g04.h>: g04dbc nag_anova_confid_interval: Perform Tukey test
		iErr = nag_anova_confid_interval( Nag_TukeyInterval, nLevels, vMEAN, dDFE, vC, nLevels, dCLevel, vCIL,
			 vCIU, vISIG );
		if( iErr != NE_NOERROR )
			return OSTAT_CONF_INT_ERROR6;

		// Output the means comparison analysis for the Tukey test
		iErr = osANOVA2Way_Means_Comparison_Output_to_ResultsLog( cFactor, strTestName, nLevels, vLEVELS,
			 vMEAN, vCIL, vCIU, vISIG, dSigLevel );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
	} // End Tukey

	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function to compute the number of data points and the mean of the dependent variable
		for each level of the current Factor ( A or B ).
	Example:
		See function osANOVA2Way_Means_Comparison above for sample call.
	Parameters:
		vFACTOR=Vector containing integerized level indices (1 based)
		nLevels=Number of levels
		vDEP_VAR=Vector containing the dependent variable
		nPts=Number of data points
		vNPTS=Vector to hold the number of data points in each level
		vMEAN=Vector to hold the mean of the dependent variable for each level
	Return:
		Returns vectors holding the number of data points and the mean of the dependent variable in each level
		of the current Factor. Also returns OSTAT_NO_ERROR on successful exit.
*/
int osANOVA2Way_Compute_MEANS_of_Levels( vector<int> &vFACTOR, int nLevels, vector<double> &vDEP_VAR, int nPts,
	 vector<int> &vNPTS, vector<double> &vMEAN )
{
	int ii, jj;
	double dPts;
	
	vNPTS = 0;
	vMEAN = 0;
	
	// Loop once on elements of vector holding dependent variable 
	for( ii = 0; ii < nPts; ii++ )
	{
		jj = vFACTOR[ii] - 1; // Compute 0 based index for level
		vNPTS[jj]++; // Count data point in level jj
		vMEAN[jj] += vDEP_VAR[ii]; // Sum data point in level jj
	}

	// Loop again computing mean of each level
	for( jj = 0; jj < nLevels; jj++ )
	{
		dPts = vNPTS[jj]; // Cast count to double 
		vMEAN[jj] /= dPts; // Compute mean of level jj
	}

	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function to output Means Comparison results to Results Log.
	Example:
		See function osANOVA2Way_Means_Comparison above for sample call.
	Parameters:
		cFactor=Factor 'A' or Factor 'B'
		strTestName=Test name: Bonferroni, Scheffe', or Tukey
		nLevels=Number of levels
		vLEVELS=Vector containing names of levels
		vMEAN=Vector containing means of levels 
		vCIL=Vector containing lower simultaneous confidence limits of levels
		vCIU=Vector containing upper simultaneous confidence limits of levels
		vISIG=Vector containing significance: 1=YES and 0=NO
		dSigLevel=Significance Level 
	Return:
		Outputs Two-Way ANOVA Means Comparison results to Results Log. Returns OSTAT_NO_ERROR
		on successful exit.
*/
int osANOVA2Way_Means_Comparison_Output_to_ResultsLog( char cFactor, string strTestName, int nLevels, vector<string> &vLEVELS,
	 vector<double> &vMEAN, vector<double> &vCIL, vector<double> &vCIU, vector<int> &vISIG, double dSigLevel )
{
	int ii, jj, iv, iRow, iCol;
	double dMeanDif;
	string strDB, strOut, strSigLevel;
	string strTablePath;
	string strTableName;	
	Worksheet wksMeansTable;
	
	// Transpose (negate and swap) vCIU and vCIL to standardize NAG output with references
	vector<double> vCILT;
	vCILT.SetSize( nLevels );
	vCILT = -1 * vCIU;

	vector<double> vCIUT;
	vCIUT.SetSize( nLevels );
	vCIUT = -1 * vCIL;
	
	// Create and attach to a temporary Two-Way ANOVA Means Comparison output worksheet
	strTablePath = GetFullPath( "osANOVA2Means.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksMeansTable.Open( strTablePath, CREATE_TEMP );
	strTableName = wksMeansTable.GetPage().GetName();
	
	// Rows and columns are indexed from 0
	
	// Output Factor and Test name in Means Comparison header in
	iRow = 0; // row 1 and column 2 of worksheet
	iCol = 1;
	strOut.Format( AN2_MEANS_COMP_HDR1, cFactor, strTestName );
	wksMeansTable.SetCell( iRow, iCol, strOut );

	// Localize significance level and the test value
	strSigLevel = LocalizeDouble( dSigLevel, OSTAT_SIG_CON_LEVEL_FORMAT );
	
	// Output Significance Level in Means Comparison header in
	iRow = 3; // row 4 and column 7 of worksheet
	iCol = 6;
	strOut.Format( AN_MEANS_COMP_AT_LEVEL, strSigLevel );
	wksMeansTable.SetCell( iRow, iCol, strOut );	
	
	// Loop on all levels in factor
	for( jj = 1; jj < nLevels; jj++ )
	{
		// Compute row of current level
		iRow++;
		if( jj != 1 ) //  For all but first level
		{
			Type_Blank_Lines_To_Wks( wksMeansTable, iRow, 2 ); // Blank out 2 rows and skip one blank row
			iRow++;
		}
		
		// Output current level name in column 2 in row of current level 
		iCol = 1;
		strOut = vLEVELS[ jj - 1 ];
		wksMeansTable.SetCell( iRow, iCol, strOut );
		
		// Output mean of current level in column 3 in row of current level 
		iCol++;
		wksMeansTable.SetCell( iRow, iCol, vMEAN[jj - 1] );
		
		// Output separator line
		iRow++;
		Type_Separator_Line_To_Wks( wksMeansTable, iRow );
		
		for( ii = jj + 1; ii <= nLevels; ii++ )
		{
			// Compute row of comparison level and initially blank row out
			iRow++;
			Type_Blank_Lines_To_Wks( wksMeansTable, iRow, 1 );
			
			// Output comparison level name in column 2 in row of comparison level
			iCol = 1;
			strOut = vLEVELS[ ii - 1 ];
			wksMeansTable.SetCell( iRow, iCol, strOut );
			
			// Output mean of comparison level in column 3 in row of comparison level 
			iCol++;
			wksMeansTable.SetCell( iRow, iCol, vMEAN[ii - 1] );

			// Output difference of means of current level minus comparison level in column 4
			iCol++;
			dMeanDif = vMEAN[jj - 1] - vMEAN[ii - 1];
			wksMeansTable.SetCell( iRow, iCol, dMeanDif );

			// Compute index into confidence limit and significance vectors			
			iv = ( ( ii - 1 ) * ( ii - 2 ) ) / 2 + ( jj - 1 );
			
			// Output lower confidence limit for difference of means in column 5
			iCol++;
			wksMeansTable.SetCell( iRow, iCol, vCILT[iv] );

			// Output upper confidence limit for difference of means in column 6
			iCol++;
			wksMeansTable.SetCell( iRow, iCol, vCIUT[iv] );

			// Output significance decision YES or NO in column 7
			iCol++;
			if( vISIG[iv] == 1 )
				strOut = AN_SIGNIFICANT_YES;
			else
				strOut = AN_SIGNIFICANT_NO;
			wksMeansTable.SetCell( iRow, iCol, strOut );
		}

		// Output separator line
		iRow++;
		Type_Separator_Line_To_Wks( wksMeansTable, iRow );
	}
		
	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output Means Comparison worksheet to Results Log
	wksMeansTable.Type( TYPETARGET_OUTPUTLOG, NULL, 8 );

	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function used to compute and output the Two-Way ANOVA Power Analysis.
	Example:
		See function osANOVA2Way above for a sample call.
	Parameter:
		iPower=Bitwise integer specifying whether or not to compute actual and hypothetical
			power: Actual (1) or Hypothetical (2)
		iInteractions=Enable (1) or disable (0) computation of Two-Way ANOVA interactions
		dPowerAlpha=Alpha used to compute Power
		nPts=Number of data points
		strSampleSize=Name of dataset holding comma separated list of hypothetical sample sizes
		arANOVA2_Table=A four element array of an ANOVA2_Row structure holding all computed F Table
			values one element for each row of the ANOVA F table (A, B, A * B, and Error)
	Return:
		Outputs the Two-Way ANOVA Power Analysis table to the Results Log. Returns OSTAT_NO_ERROR
		on successful exit or an OSTAT error code on failure.
*/
int osANOVA2Way_Power( int iPower, int iInteractions, double dPowerAlpha, int nPts, string strSampleSizes,
	 ANOVA2_Row *arANOVA2_Table )
{
	int iErr, iSize, iSource;
	double dAlpha, dPower;
	string strTablePath;
	string strTableName;

	// Vector to hold hypothetical sample sizes
	vector<double> vSS;
	
	// Vector to hold hypothetical powers
	vector<double> vSSPOW;
	iSize = 0;
	
	// If computation of hypothetical powers is enabled...
	if( iPower & 2 )
	{
		Dataset dsSS( strSampleSizes ); // Attach to dataset holding hypothetical sample sizes 
		iSize = dsSS.GetSize();         //  and copy sample sizes to vector
		vSS.SetSize( iSize );
		vSS = dsSS;
		dsSS.Detach();                  // No longer need dataset 
	
		vSSPOW.SetSize( iSize );        // One power for each hypothetical sample size
	}
	
	dAlpha = 1 - dPowerAlpha; // Compute power alpha
	
	
	Worksheet wksPowerTable; // Temporary output worksheet used for all sources
	// Create and attach to a temporary Two-Way ANOVA Power output worksheet
	strTablePath = GetFullPath( "osANOVA2Power.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
	wksPowerTable.Open( strTablePath, CREATE_TEMP );
	strTableName = wksPowerTable.GetPage().GetName();

	// Compute Powers for Source A
	iSource = 0;	
	iErr = osANOVA2Way_Compute_Power( iPower, iInteractions, dAlpha, nPts, iSize,
		 &arANOVA2_Table[0], iSource, vSS, dPower, vSSPOW );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;

	// Output Powers for Source A to temporary output worksheet
	iErr = osANOVA2Way_Power_Output_to_ResultsLog( iInteractions, iSource, dPowerAlpha, nPts, dPower, iPower,
		 vSS, vSSPOW, wksPowerTable );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;	
	
	// Compute Powers for Source B
	iSource = 1;
	iErr = osANOVA2Way_Compute_Power( iPower, iInteractions, dAlpha, nPts, iSize,
		 &arANOVA2_Table[0], iSource, vSS, dPower, vSSPOW );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;

	// Output Powers for Source B to temporary output worksheet
	iErr = osANOVA2Way_Power_Output_to_ResultsLog( iInteractions, iSource, dPowerAlpha, nPts, dPower, iPower,
		 vSS, vSSPOW, wksPowerTable );
	if( iErr != OSTAT_NO_ERROR )
		return iErr;
	
	// If iInteractions is enabled (==1)...
	if( iInteractions )
	{
		// Compute Powers for Source A * B 
		iSource = 2;
		iErr = osANOVA2Way_Compute_Power( iPower, iInteractions, dAlpha, nPts, iSize,
			 &arANOVA2_Table[0], iSource, vSS, dPower, vSSPOW );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;
		
		// Output Powers for Source A * B to temporary output worksheet
		iErr = osANOVA2Way_Power_Output_to_ResultsLog( iInteractions, iSource, dPowerAlpha, nPts, dPower, iPower,
			 vSS, vSSPOW, wksPowerTable );
		if( iErr != OSTAT_NO_ERROR )
			return iErr;		
	}
	
	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send temporary output Power worksheet to Results Log
	wksPowerTable.Type( TYPETARGET_OUTPUTLOG, NULL, 6 );

	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function used to compute the Two-Way ANOVA Power Analysis.
	Example:
		See function osANOVA2Way above for a sample call.
	Parameter:
		iPower=Bitwise flag indicating whether no power (0), only actual power (1), or actual and
			hypothetical powers (3) are to be output 
		iInteractions=Flag to enable (1) or disable (0) computation of Two-Way ANOVA interactions
		dAlpha=Alpha used to compute Power
		nPts=Number of data points or actual sample size
		iSize=Number of elements in hypothetical sample sizes vector vSS
		arANOVA2_Table=A four element array of an ANOVA2_Row structure holding all computed F Table
			values one element for each row of the ANOVA F table (A, B, A * B, and Error)
		iSource=ANOVA source for which powers are being computed: Source A (0), Source B (1), Source  A * B (2)
		vSS=Vector containing the hypothetical sample sizes
		dPower=Output value for actual power
		vSSPOW=Output vector containing hypothetical powers	
	Return:
		Returns the actual and hypothetical powers for the current ANOVA source. Returns OSTAT_NO_ERROR
		on successful exit or an OSTAT error code on failure.
*/
int osANOVA2Way_Compute_Power( int iPower, int iInteractions, double dAlpha, int nPts, int iSize,
	 ANOVA2_Row *arANOVA2_Table, int iSource, vector<double> &vSS, double &dPower, vector<double> &vSSPOW )
{
	int ii, iErr, iAlpha;
	double dDevF, dNC;
	string strFTable;
	BOOL bErr;
	
	NagError neErr;
	NagError *pneFail = &neErr;		// NAG error structure

	// Compute actual F deviate...use LabTalk ftable function in place of NAG call to avoid tolerance issue
	iAlpha = dAlpha * OSTAT_ROUND_FACTOR1 + 0.5;
	strFTable.Format( "ftable(%d/%d,%d,%d)", iAlpha, OSTAT_ROUND_FACTOR1, (int) arANOVA2_Table[iSource].dDF, (int) arANOVA2_Table[3].dDF );
	bErr = LT_evaluate( strFTable, &dDevF );
	if( bErr == FALSE )
		return OSTAT_NAG_F_DEVIATE_ERROR1;

	// Compute actual non-centrality factor 
	dNC = arANOVA2_Table[iSource].dSS / arANOVA2_Table[3].dMS;
	
	// Compute actual power
	dPower = osANOVA_Power( dDevF, arANOVA2_Table[iSource].dDF, arANOVA2_Table[3].dDF, dNC );
	if( dPower == NANUM )
		return OSTAT_ANOVA2_POWER_ERROR1;

	// If hypothetical power computations are requested...
	if( iPower & 2 )
	{
		double dSS, dnPts, dssNC, dssDFE, dssDevF;
		
		// Loop once for each hypothetical sample size
		for( ii = 0; ii < iSize; ii++ )
		{
			// Compute non-centrality factor for hypothetical sample size
			dSS = (double) vSS[ii];
			dnPts = (double) nPts;
			dssNC = ( dSS / dnPts ) * dNC;
			
			// Compute the Degrees of Freedom of Error for hypothetical sample size 
			if( iInteractions )
				dssDFE = dSS - ( arANOVA2_Table[0].dDF + 1 ) * ( arANOVA2_Table[1].dDF + 1 );
			else
				dssDFE = dSS - arANOVA2_Table[0].dDF - arANOVA2_Table[1].dDF - 1;
			if( dssDFE < 1 )
				return OSTAT_ANOVA2_POWER_ERROR2;
			else
			{
				// Compute hypothetical F deviate...use LabTalk ftable function in place of NAG call to avoid tolerance issue			
				strFTable.Format( "ftable(%d/%d,%d,%d)", iAlpha, OSTAT_ROUND_FACTOR1, (int) arANOVA2_Table[iSource].dDF, (int) dssDFE );
				bErr = LT_evaluate( strFTable, &dssDevF );
				if( bErr == FALSE )
					return OSTAT_NAG_F_DEVIATE_ERROR2;

				// Compute power for hypothetical sample size
				vSSPOW[ii] = osANOVA_Power( dssDevF, arANOVA2_Table[iSource].dDF, dssDFE, dssNC );
				if( vSSPOW[ii] == NANUM )
					return OSTAT_ANOVA2_POWER_ERROR3;
			} // End else
		} // End loop on hypothetical sample sizes
	} // End if hypothetical powers requested

	return OSTAT_NO_ERROR;
}

/**
		Two-Way ANOVA function to output Power values to the Results Log.
	Example:
		See function osANOVA2Way_Power above for sample call.
	Parameters:
		iInteractions=Flag indicating whether (1) or not (0) interactions were included
			in ANOVA computations
		iSource=Flag indicating the Source A (0), B (1), or A * B (2)
		dPowerAlpha=Alpha used in power computations
		nPts=Actual sample size
		dPower=Actual power value
		iPower=Bitwise flag indicating whether no power (0), only actual power (1), or actual and
			hypothetical powers (3) are to be output 
		vSS=Vector containing hypothetical sample sizes
		vSSPOW=Vector containing hypothetical powers
		wksPowerTable=Output temporary power worksheet used across multiple calls
	Return:
		Outputs Two-Way ANOVA Power values to temporary power worksheet. Returns OSTAT_NO_ERROR
		on successful exit.
*/
int osANOVA2Way_Power_Output_to_ResultsLog( int iInteractions, int iSource, double dPowerAlpha, int nPts,
	 double dPower, int iPower, vector<double> &vSS, vector<double> &vSSPOW, Worksheet &wksPowerTable )
{
	int ii, iSize, iSS;
	string strSource;
	int iCol;
	static int iRow; // Current row in output worksheet used across repeated calls of function
	
	// All row and column numbers are indexed from 0
	
	if( iSource == 0 ) // If Source is A...
	{
		iRow = 5; // Output actual power of source A starting in row 6
		strSource = AN2_TABLE_SOURCE_A;
	}
	else
	{
		// Output a blank line to separate power values from different sources
		iRow++;
		Type_Blank_Lines_To_Wks( wksPowerTable, iRow, 1 );
		
		// Output actual power of current source (B or A * B) starting in next row
		iRow++;
		if( iSource == 1 )
			strSource = AN2_TABLE_SOURCE_B;
		else
			strSource = AN2_TABLE_SOURCE_AxB;			
	}
		
	// Initially blank out and then output values in actual power row
	Type_Blank_Lines_To_Wks( wksPowerTable, iRow, 1 );
	
	// Output source in column 2
	iCol = 1;
	wksPowerTable.SetCell( iRow, iCol, strSource );
	
	// Output alpha in column 3
	iCol++;
	wksPowerTable.SetCell( iRow, iCol, dPowerAlpha );

	// Output actual sample size in column 4
	iCol++;
	wksPowerTable.SetCell( iRow, iCol, nPts );
		
	// Output actual power in column 5
	iCol++;
	wksPowerTable.SetCell( iRow, iCol, dPower );
	
	// Output "(actual)" in column 6
	iCol++;
	wksPowerTable.SetCell( iRow, iCol, OSTAT_POWER_ACTUAL );
	
	if( iPower & 2 ) // If hypothetical powers were computed...
	{
		iSize = vSS.GetSize(); // Get number of hypothetical sample sizes and then loop through them
		for( ii = 0; ii < iSize; ii++ )
		{
			// Compute row for current hypothetical power/sample size
			iRow++;
			
			// Initially blank out and then output values in current hypothetical power row
			Type_Blank_Lines_To_Wks( wksPowerTable, iRow, 1 );
	
			// Output source in column 2
			iCol = 1;
			wksPowerTable.SetCell( iRow, iCol, strSource );
	
			// Output alpha in column 3
			iCol++;
			wksPowerTable.SetCell( iRow, iCol, dPowerAlpha );

			// Output hypothetical sample size in column 4
			iCol++;
			iSS = vSS[ii];
			wksPowerTable.SetCell( iRow, iCol, iSS );
		
			// Output hypothetical power in column 5
			iCol++;
			wksPowerTable.SetCell( iRow, iCol, vSSPOW[ii] );
		}
	}
	
	if( iSource == 2 || ( iSource == 1 && !iInteractions ) ) // If finished with worksheet...
	{
		// Output closing separator row for power table 
		iRow++;
		iCol = 5;
		Type_Separator_Line_To_Wks( wksPowerTable, iRow );
		wksPowerTable.SetCell( iRow, iCol, OSTAT_EIGHT_DASHES ); // Wide last column to accomodate "(actual)"	
	}

	return OSTAT_NO_ERROR;	
}

/**
		Two-Way ANOVA utility function to get localized variable names.
	Example:
		See the [VariableTypeBTN.OnClick] section in the ANOVA2Way.OGS file for a sample call. 
	Parameters:
		iVarTypeCode=Code for variable name to return. Valid codes are AN2_DEP_VAR_CODE (1),
			AN2_FACTOR_A_CODE (2), or AN2_FACTOR_B_CODE (3).
	Return:
		Returns a mixed case localized variable name for the passed code.  Returns
		"--" if code is not legal (other than 1, 2, or 3).
*/
string osANOVA2Way_Get_Localized_Var_Name( int iVarTypeCode )
{
	string strVarType;
	
	switch( iVarTypeCode )
	{
		case AN2_DEP_VAR_CODE:
			strVarType = AN2_DEPENDENT_VAR; // Dependent Variable
			break;
		case AN2_FACTOR_A_CODE:
			strVarType = AN2_FACTOR_A_VAR; // Factor A Classification Variable
			break;
		case AN2_FACTOR_B_CODE:
			strVarType = AN2_FACTOR_B_VAR; // Factor B Classification Variable
			break;
		default:
			strVarType = "--";
	}

	return strVarType;
}

////////////////////////////////////////////////////////////////////////////////////
/**
		Performs the Shapiro-Wilk test for Normality on a list of datasets (each of
		size N where 3<=N<=2000) at a single level of significance. The computational
		engine is the NAG function nag_shapiro_wilk_test (g01ddc) which cites the Applied
		Statistics Algorithm AS 181 (see Royston 1982a and 1982b) as its reference.     
	Example:
		See the [NormalityTest] section of the WKSSTAT.OGS file for a sample call.
	Parameters:
		strDatasets=List of names of datasets on which to perform test
		dNormTestSL=Significance level of test 
	Return:
		Outputs results of Shapiro-Wilk test statistic W and P value to Results Log and
		returns OSTAT_NO_ERROR on successful exit or an OSTAT error code on failure.
*/
int osNormalityTest( string strDatasets, double dNormTestSL )
{
	BOOL		bErr;
	Worksheet	wksNormalityTable;
	string		strTablePath;
	string		strTableName;
	string		strThisDataset;
	int			iRow;
	int			iCol;
	int			iCount;
	int			iStatus = 0;
	
	// Create and attach to a temporary Normality output worksheet
	strTablePath = GetFullPath( "osNormShapWilkTable.OGW", OSTAT_OGW_SUBFOLDER, TRUE );
	if( !strTablePath.IsFile() )
	{
		Type_ErrorMsg1( OSTAT_FILE_NOT_FOUND_ERROR_MSG, strTablePath );
		return OSTAT_ABORT_NO_ERROR_MSG;
	}
    wksNormalityTable.Open( strTablePath, CREATE_TEMP );
    strTableName = wksNormalityTable.GetPage().GetName();

	// Set Output redirection to Results Log and Begin Results
    LT_execute( "type.redirection=16;type.BeginResults();" );

   	// Loop on all input raw datasets to test
	iCount = strDatasets.GetNumTokens();
	for( iCount = 0, iRow = 4; iCount < strDatasets.GetNumTokens(); iCount++, iRow++ )
	{
		strThisDataset = strDatasets.GetToken( iCount );
		Dataset dsRawDATA( strThisDataset );

		wksNormalityTable.SetCell( iRow, 0, " " );
		wksNormalityTable.SetCell( iRow, 1, strThisDataset );
		bErr = dsRawDATA.IsValid();
		if( bErr == FALSE )
		{
			// If Text dataset GetName fails...fail here with error message
			wksNormalityTable.SetCell( iRow, 5, NT_GET_DATA_NAME_ERROR );
			wksNormalityTable.SetCell( iRow, 6, " " );
			iStatus = OSTAT_GET_DATA_NAME_ERROR;
		}
		else
		{	// Else dataset name OK so continue...
			int iErr;
			double W, PW;
			string strPW, strDecision;

			// Create temporary copy of input dataset for sorting...don't want to change raw data
			Dataset dsSortedDATA( dsRawDATA );

			// Sort copied dataset
			bErr = Data_sort( &dsSortedDATA );
			if( bErr == FALSE )
			{
				// If sort fails quit here with error message
				wksNormalityTable.SetCell( iRow, 5, NT_SORT_ERROR );
				wksNormalityTable.SetCell( iRow, 6, " " );
				iStatus = OSTAT_DATA_SORT_ERROR1;
			}
			else
			{	// Else sort OK so continue...
				int	iSize;

				// Put sorted dataset in vector to remove missing and masked values and to pass to NAG function
				vector<double> vSortedDATA( dsSortedDATA, TRUE );
				
				// Datasets are no longer needed
				dsRawDATA.Detach();
				dsSortedDATA.Detach();	

				// Get and output size
				iSize = vSortedDATA.GetSize();
				wksNormalityTable.SetCell( iRow, 2, iSize );

				// NAG function requires 2000 > iSize > 2
				if( iSize < 3 || iSize > 2000 )
				{
					if( iSize < 3 )
					{
						// If dataset is to small fail here with error message
						wksNormalityTable.SetCell( iRow, 5, NT_TOO_FEW_DATAPTS );
						wksNormalityTable.SetCell( iRow, 6, " " );
						iStatus = OSTAT_TOO_FEW_DATAPTS_WARNING;
					}
					else
					{
						// If dataset is to large fail here with error message
						wksNormalityTable.SetCell( iRow, 5, NT_TOO_MANY_DATAPTS );
						wksNormalityTable.SetCell( iRow, 6, " " );
						iStatus = OSTAT_TOO_MANY_DATAPTS_WARNING;
					}
				}
				else
				{	// Else size OK so continue...
					
					// Weights for NAG function, NAG computes for itself but still needs vector   
					vector<double> vA;
					vA.SetSize( iSize );
		
					// From <NAG\OCN_g01.h>: g01ddc nag_shapiro_wilk_test: Conduct Normality Test
					iErr = nag_shapiro_wilk_test( iSize, vSortedDATA, TRUE, vA, &W, &PW );
					if( iErr != NE_NOERROR )
					{
						// Stop and return error if NAG function returns error
						wksNormalityTable.SetCell( iRow, 5, NT_SHAPIRO_WILK_ERROR );
						iStatus = OSTAT_SHAPIRO_WILK_ERROR;
						wksNormalityTable.SetCell( iRow, 6, " " );
					}
					else
					{	// Else NAG Success so continue... 
						// Make decision rule based on passed significance level and P value	
						if( PW > dNormTestSL )
							strDecision = NT_NORMAL_AT + LocalizeDouble( dNormTestSL, OSTAT_SIG_CON_LEVEL_FORMAT ) + NT_AT_LEVEL;     // Dataset is Normal
						else
							strDecision = NT_NOT_NORMAL_AT + LocalizeDouble( dNormTestSL, OSTAT_SIG_CON_LEVEL_FORMAT ) + NT_AT_LEVEL; // Dataset is not Normal

						// Output iSize, W statistic, P value, and decision to Results Log
						wksNormalityTable.SetCell( iRow, 2, iSize );
						wksNormalityTable.SetCell( iRow, 3, W );
						wksNormalityTable.SetCell( iRow, 4, PW );
						wksNormalityTable.SetCell( iRow, 5, strDecision );
						wksNormalityTable.SetCell( iRow, 6, " " );
					}	// END NAG Success
				}	// END Size OK
			}	// END Sort OK
		}	// END Dataset name OK
	}	// END for loop for all datasets, increment iRow and iCount
	
	// Output closing separator line for table
	wksNormalityTable.SetCell( iRow, 0, " " );
	for( iCol = 1 ; iCol <= 5 ; iCol++ )
	{
		wksNormalityTable.SetCell( iRow, iCol, "---" );
	}
	wksNormalityTable.SetCell( iRow, iCol, OSTAT_TEN_DASHES );

	// GJL 11/14/02 v7.0434 USE_OC_WKS_TYPE_NOT_LT
	// Send output Normality Test worksheet to Results Log	
	wksNormalityTable.Type( TYPETARGET_OUTPUTLOG, NULL, 7 );

	return iStatus;
}