/***********************************************************
 *Mediation Analysis                                       *
 *                                                         *
 *   Author: Zhiyong Zhang                                 *
 *                                                         *
 *   Purpose: Test mediation through bootstrap and EM      *
 *            algorithm for missing data                   *
 *                                                         *
 *   Usage:                                                *
 *      Run the program and the message appears            *
 ***********************************************************/
 
/***********************************************************
Change history:
2008/12/20 
   Version 1 based on the BootMed
 
 **********************************************************/
 
 
// The library newmat was used for the matrix operation    //
#define WANT_STREAM
#define WANT_MATH
#define WANT_FSTREAM
 
#include "newmatap.h"
#include "newmatio.h"
#include "newmatnl.h"
#include "newran.h"
#include "math.h"
 
#include<iostream>
#include<fstream>
#include<ostream>
#include<string>
#include "time.h"
 
#ifdef use_namespace
using namespace RBD_LIBRARIES;
#endif
 
/*------------------------------------------------------*\ 
 * Normal quantile calculation function                 *
\*------------------------------------------------------*/ 
double dfQNorm(double alpha){
	ColumnVector ck(100);
	ck=0.0;
	ck(1)=1;
	ck(2)=1;
	for (int k=3; k<=100; k++){
		for (int m=1; m<k; m++){
			ck(k)=ck(k)+ck(m)*ck(k-m)/((m)*(2.0*m-1.0));
		}
	}
 
	double x;
	x=2.0*alpha-1.0;
 
	double pi=3.14159265358979323846;
 
	double erf=0.0;
	for (int i=1; i<=100; i++){
	   erf=erf+ck(i)/(2.0*i-1.0)*pow((sqrt(pi)/2.0*x),(2.0*i-1.0));
	}
 
	erf=sqrt(2.0)*erf;
 
	return erf;
} 
 
// factorial fucntion
double ifFactor(int N){
	double iFactor=1.0;
	for (int i=1; i<=N; i++){
		iFactor = iFactor * i;
	}
	return iFactor;
}
/*------------------------------------------------------*\ 
 * Normal cumulative distribution function              *
\*------------------------------------------------------*/ 
double dfPNorm(double dX){
 
	double dZ = dX/sqrt(2.0);
 
	double pi=3.14159265358979323846;
 
    double erf=dZ;
	for (int i=1; i<=100; i++){
	   erf=erf+pow(-1.0,i)*pow(dZ, 2.0*i+1.0)/(ifFactor(i)*(2.0*i+1.0));
	}
	erf=2.0*erf/sqrt(pi);
	erf=(1.0+erf)/2.0;
 
	return erf;
} 
 
/*------------------------------------------------------*\ 
 * Function to read in a data file and decide the size. *
 * Read in the file name and output the row and column  *
 * Numbers.                                             *
\*------------------------------------------------------*/ 
 
void size(char *filename, int &rn, int &cn)
{
	Tracer tr("size"); // for tracking exceptions
    //Open file and decide the size of the file
	std::ifstream in;
	in.open(filename);
	if (!in) { std::cout << "cannot open the data file\n"; exit(1); }
 
	std::string str;
    std::string buf;
 
	 // Decide the number of rows
	int row=0;
 
    while(std::getline(in,str))
    {
        if(row==0)
            buf=str;
        row++;
	//	std::cout<<str<<"\n";
    }
 
	// Number of columns
	//c_str() convert a string to char*
 
    int column=0;
    const char *c=buf.c_str();
    bool tag=true;
    while(*c!='\0')
    {
        if(*c!=' '&&tag==true)
    {
        c++;
        tag=false;
        column++;
    }
    else if(*c++==' ')
    {
            tag=true;
    }
    }
 
	rn=row;
    cn=column;
 
	in.close();
 
}
 
/*------------------------------------------------------*\ 
 * Read the data from a file into a matrix              *
\*------------------------------------------------------*/ 
 
Matrix readdata(char *filename, int row, int col){
	std::ifstream fin;
	fin.open(filename);
 
	if (!fin) { std::cout << "cannot open the data file\n"; exit(1); }
 
	Matrix X(row,col);
 
	for (int i=1; i<=row; i++)
		for (int j=1; j<=col; j++)
		{
			fin >> X(i,j);
		}
	fin.close();
	return X;
}
 
/*------------------------------------------------------*\ 
 * Function to organize the data using missing pattern  *
 * Return the matrix of data and vector of pattern size *
 * Input: matData: original data                        *
 *        iN: sample size                               *
 * Output: cvN: a vector with pattern size              *
 *         matPatData: sorted data with missing pattern *
\*------------------------------------------------------*/ 
void pattern(Matrix matData, int iN, ColumnVector &cvN, Matrix &matPatData){
	int n1=0,n2=0,n3=0,n4=0,n5=0,n6=0,n7=0,n8=0;
	double dmiss=99999.0;
	//Get the dimension of different patterns
	for (int i1=1; i1<=iN; i1++){
		if (matData(i1,1)!=dmiss){
			if (matData(i1,2)!=dmiss){
				if (matData(i1,3)!=dmiss){
					n1=n1+1;
				}else{
					n2=n2+1;
				}
			}else{
				if (matData(i1,3)!=dmiss){
					n3=n3+1;
				}else{
					n4=n4+1;
				}
			}
		}else{
			if (matData(i1,2)!=dmiss){
				if (matData(i1,3)!=dmiss){
					n5=n5+1;
				}else{
					n6=n6+1;
				}
			}else{
				if (matData(i1,3)!=dmiss){
					n7=n7+1;
				}else{
					n8=n8+1;
				}
			}
		}
	}//end of the sample size for each pattern
 
	//Define the matrix to store the data for different patterns
	int m1=n1, m2=n2, m3=n3, m4=n4, m5=n5, m6=n6, m7=n7, m8=n8;
 
	if (n1==0){m1=1;}
	if (n2==0){m2=1;}
	if (n3==0){m3=1;}
	if (n4==0){m4=1;}
	if (n5==0){m5=1;}
	if (n6==0){m6=1;}
	if (n7==0){m7=1;}
	if (n8==0){m8=1;}
 
	Matrix matPat1(iN,3);
	Matrix matPat2(iN,3);
	Matrix matPat3(iN,3);
	Matrix matPat4(iN,3);
	Matrix matPat5(iN,3);
	Matrix matPat6(iN,3);
	Matrix matPat7(iN,3);
	Matrix matPat8(iN,3);
 
	matPat1=0.0;
	matPat2=0.0;
	matPat3=0.0;
	matPat4=0.0;
	matPat5=0.0;
	matPat6=0.0;
	matPat7=0.0;
	matPat8=0.0;
 
	//Get the dimension of different patterns
	int k1=1, k2=1, k3=1, k4=1, k5=1, k6=1, k7=1, k8=1;
	for (int i2=1; i2<=iN; i2++){
		if (matData(i2,1)!=dmiss){
			if (matData(i2,2)!=dmiss){
				if (matData(i2,3)!=dmiss){
					matPat1.row(k1)=matData.row(i2);
					k1++;
				}else{
					matPat2.row(k2)=matData.row(i2);
					k2++;
				}
			}else{
				if (matData(i2,3)!=dmiss){
					matPat3.row(k3)=matData.row(i2);
					k3++;
				}else{
					matPat4.row(k4)=matData.row(i2);
					k4++;
				}
			}
		}else{
			if (matData(i2,2)!=dmiss){
				if (matData(i2,3)!=dmiss){
					matPat5.row(k5)=matData.row(i2);
					k5++;
				}else{
					matPat6.row(k6)=matData.row(i2);
					k6++;
				}
			}else{
				if (matData(i2,3)!=dmiss){
					matPat7.row(k7)=matData.row(i2);
					k7++;
				}else{
					matPat8.row(k8)=matData.row(i2);
					k8++;
				}
 
			}
		}
	}//end of the data pattern
	//put the data into the pattern size vector
	cvN<<n1<<n2<<n3<<n4<<n5<<n6<<n7<<n8;
 
	//put the data into the pattern matrix
	int l2=n1+n2;
	int l3=l2+n3;
	int l4=l3+n4;
	int l5=l4+n5;
	int l6=l5+n6;
	int l7=l6+n7;
 
	int i3;
	if (n1>0){
		for (i3=1; i3<=n1; i3++){
			matPatData.row(i3)=matPat1.row(i3);
	    }
	}
	if (n2>0){
		for (i3=1; i3<=n2; i3++){
			matPatData.row(n1+i3)=matPat2.row(i3);
	    }
	}
	if (n3>0){
		for (i3=1; i3<=n3; i3++){
			matPatData.row(l2+i3)=matPat3.row(i3);
	    }
	}
	if (n4>0){
		for (i3=1; i3<=n4; i3++){
			matPatData.row(l3+i3)=matPat4.row(i3);
	    }
	}
	if (n5>0){
		for (i3=1; i3<=n5; i3++){
			matPatData.row(l4+i3)=matPat5.row(i3);
	    }
	}
	if (n6>0){
		for (i3=1; i3<=n6; i3++){
			matPatData.row(l5+i3)=matPat6.row(i3);
	    }
	}
	if (n7>0){
		for (i3=1; i3<=n7; i3++){
			matPatData.row(l6+i3)=matPat7.row(i3);
	    }
	}
	if (n8>0){
		for (i3=1; i3<=n8; i3++){
			matPatData.row(l7+i3)=matPat8.row(i3);
	    }
	}
 
}
 
/*------------------------------------------------------*\ 
 * Function to calculate the mean and covariance matrix *
 * Output: mCov
           mCov[1:c,r] is the the means
		   mCov[1:c,1:r-1] is the covariance
\*------------------------------------------------------*/ 
Matrix mfCov(Matrix matX, int iRow, int iCol){
	RowVector vMean = matX.sum_columns()/iRow;
	Matrix mXbar(iRow,iCol);
	for (int i=1; i<=iRow; i++){
		mXbar.row(i)=vMean;
	}
    Matrix mDesign=matX-mXbar;
	Matrix mCov(iCol+1, iCol);
	mCov.submatrix(1,iCol,1,iCol)=mDesign.t()*mDesign/(iRow-1.0);
	mCov.row(iCol+1)=vMean;
 
	return mCov;
}
 
/*------------------------------------------------------*\ 
 * Function to estimate the coefficients                *
\*------------------------------------------------------*/
 
RowVector cvfParas(Matrix mCov)
{
	double d_a1, d_a2, d_b1, d_sig1, d_sig2, d_sigx, d_a2b1, dybar, dmbar, dxbar;
	double d_sy, d_sx, d_sm, d_sxy, d_sxm, d_sym;
	double d_mM, d_mY;
	RowVector cvPara(8);
 
	d_sy=mCov(3,3);
	d_sx=mCov(1,1);
	d_sm=mCov(2,2);
	d_sxy=mCov(1,3);
	d_sym=mCov(2,3);
	d_sxm=mCov(1,2);
 
	dybar=mCov(4,3);
	dxbar=mCov(4,1);
	dmbar=mCov(4,2);
 
	if (d_sxm*d_sxm-d_sm*d_sx==0){
		cout<<"Problem in calculation"<<endl;
		exit(1);
	}else{
 
	d_a1=(d_sxm*d_sym-d_sm*d_sxy)/(d_sxm*d_sxm-d_sm*d_sx);
	d_a2=(d_sx*d_sym-d_sxm*d_sxy)/(d_sm*d_sx-d_sxm*d_sxm);
	d_b1=d_sxm/d_sx;
	d_a2b1=d_a2*d_b1;
 
	d_sigx=d_sx;
	d_sig1=d_sm-d_sxm*d_sxm/d_sx;
	d_sig2=(d_sm*d_sx*d_sy-d_sxm*d_sxm*d_sy-d_sx*d_sym*d_sym-d_sm*d_sxy*d_sxy+2*d_sxm*d_sym*d_sxy)/(d_sm*d_sx-d_sxm*d_sxm);
 
	d_mM = dmbar - dxbar*d_b1;
	d_mY = dybar - d_a1*dxbar - d_a2*dmbar;
 
	cvPara(1)=d_mM;
	cvPara(2)=d_mY;
	cvPara(3)=d_b1;
	cvPara(4)=d_a2;
	cvPara(5)=d_a1; //variance for residuals of M~X
	cvPara(6)=d_sig1; //variance for residuals of Y~X+M
	cvPara(7)=d_sig2;
	cvPara(8)=d_a2b1;
	}
 
	return cvPara;
}
 
/*------------------------------------------------------*\ 
 * EM algorithm - The M-step
 * Output: mCov -- covariance matrix and means
\*------------------------------------------------------*/ 
Matrix mfMstep(Matrix mSS, int iN){
	Matrix mSSV(3,3);
	RowVector mSSM(3);
	mSSV = mSS.submatrix(1,3,1,3);
	mSSM = mSS.row(4);
 
	RowVector mMean=mSSM/iN;
	Matrix mCov(4,3);
	mCov.submatrix(1,3,1,3) = mSSV/(iN-1.0) - iN/(iN-1.0)*mMean.t()*mMean;
	mCov.row(4)=mMean;
 
	return mCov;
}
 
/*------------------------------------------------------*\ 
 * EM algorithm - The E-step
 * Output: mCov -- covariance matrix and means
\*------------------------------------------------------*/ 
 
Matrix mfEstep(Matrix matData, Matrix mCov, int iN){
	ColumnVector sX2(iN), sM2(iN),sY2(iN),sXM(iN),sXY(iN),sMY(iN),mX(iN),mM(iN),mY(iN);
	int i;
	double dmiss=99999.0;
	//Get the dimension of different patterns
	for (i=1; i<=iN; i++){
		if (matData(i,1)!=dmiss){
			if (matData(i,2)!=dmiss){
				if (matData(i,3)!=dmiss){
					mX(i)=matData(i,1);
					mM(i)=matData(i,2);
					mY(i)=matData(i,3);
					sX2(i)=mX(i)*mX(i);
					sY2(i)=mY(i)*mY(i);
					sM2(i)=mM(i)*mM(i);
					sXM(i)=mX(i)*mM(i);
					sXY(i)=mX(i)*mY(i);
					sMY(i)=mM(i)*mY(i);
				}else{
					mX(i)=matData(i,1);
					mM(i)=matData(i,2);
					mY(i)=mCov(4,3) + ( (mCov(3,1)*mCov(2,2)-mCov(3,2)*mCov(1,2))*(mX(i)-mCov(4,1))+
						                (mCov(3,2)*mCov(1,1)-mCov(3,1)*mCov(1,2))*(mM(i)-mCov(4,2)))/(
										 mCov(1,1)*mCov(2,2)-mCov(1,2)*mCov(1,2));
					sX2(i)=mX(i)*mX(i);
					sY2(i)=mY(i)*mY(i)+ mCov(3,3)-( (mCov(3,1)*mCov(2,2)-mCov(3,2)*mCov(1,2))*mCov(3,1)+
						                (mCov(3,2)*mCov(1,1)-mCov(3,1)*mCov(1,2))*mCov(3,2))/(
										 mCov(1,1)*mCov(2,2)-mCov(1,2)*mCov(1,2));
					sM2(i)=mM(i)*mM(i);
					sXM(i)=mX(i)*mM(i);
					sXY(i)=mX(i)*mY(i);
					sMY(i)=mM(i)*mY(i);
				}
			}else{
				if (matData(i,3)!=dmiss){
					mX(i)=matData(i,1);
					mY(i)=matData(i,3);
					mM(i)=mCov(4,2)+( (mCov(2,1)*mCov(3,3)-mCov(3,2)*mCov(1,3))*(mX(i)-mCov(4,1))+
						                (mCov(3,2)*mCov(1,1)-mCov(3,1)*mCov(1,2))*(mY(i)-mCov(4,3)))/(
										 mCov(1,1)*mCov(3,3)-mCov(1,3)*mCov(1,3));
					sX2(i)=mX(i)*mX(i);
					sY2(i)=mY(i)*mY(i);
					sM2(i)=mM(i)*mM(i)+mCov(2,2)-( (mCov(2,1)*mCov(3,3)-mCov(3,2)*mCov(1,3))*mCov(2,1)+
						                (mCov(3,2)*mCov(1,1)-mCov(3,1)*mCov(1,2))*mCov(2,3))/(
										 mCov(1,1)*mCov(3,3)-mCov(1,3)*mCov(1,3));
					sXM(i)=mX(i)*mM(i);
					sXY(i)=mX(i)*mY(i);
					sMY(i)=mM(i)*mY(i);
				}else{
					mX(i)=matData(i,1);
					mM(i)=mCov(4,2)+mCov(1,2)/mCov(1,1)*(mX(i)-mCov(4,1));
					mY(i)=mCov(4,3)+mCov(1,3)/mCov(1,1)*(mX(i)-mCov(4,1));
					sX2(i)=mX(i)*mX(i);
					sY2(i)=mY(i)*mY(i)+mCov(3,3)-mCov(1,3)*mCov(1,3)/mCov(1,1);
					sM2(i)=mM(i)*mM(i)+mCov(2,2)-mCov(1,2)*mCov(1,2)/mCov(1,1);
					sXM(i)=mX(i)*mM(i);
					sXY(i)=mX(i)*mY(i);
					sMY(i)=mM(i)*mY(i)+mCov(2,3)-mCov(1,2)*mCov(1,3)/mCov(1,1);
				}
			}
		}else{
			if (matData(i,2)!=dmiss){
				if (matData(i,3)!=dmiss){
					mM(i)=matData(i,2);
					mY(i)=matData(i,3);
					mX(i)=mCov(4,1) + ( (mCov(2,1)*mCov(3,3)-mCov(3,2)*mCov(1,3))*(mM(i)-mCov(4,2))+
						                (mCov(1,3)*mCov(2,2)-mCov(3,2)*mCov(1,2))*(mY(i)-mCov(4,3)))/(
										 mCov(2,2)*mCov(3,3)-mCov(2,3)*mCov(2,3));
					sX2(i)=mX(i)*mX(i) + mCov(1,1)-( (mCov(2,1)*mCov(3,3)-mCov(3,2)*mCov(1,3))*(mCov(1,2))+
						                (mCov(1,3)*mCov(2,2)-mCov(3,2)*mCov(1,2))*(mCov(1,3)))/(
										 mCov(2,2)*mCov(3,3)-mCov(2,3)*mCov(2,3));
					sY2(i)=mY(i)*mY(i);
					sM2(i)=mM(i)*mM(i);
					sXM(i)=mX(i)*mM(i);
					sXY(i)=mX(i)*mY(i);
					sMY(i)=mM(i)*mY(i);
				}else{
					mM(i)=matData(i,2);
					mX(i)=mCov(4,1)+mCov(1,2)/mCov(2,2)*(mM(i)-mCov(4,2));
					mY(i)=mCov(4,3)+mCov(2,3)/mCov(2,2)*(mM(i)-mCov(4,2));
					sX2(i)=mX(i)*mX(i)+mCov(1,1)-mCov(1,2)*mCov(1,2)/mCov(2,2);
					sY2(i)=mY(i)*mY(i)+mCov(3,3)-mCov(3,2)*mCov(3,2)/mCov(2,2);
					sM2(i)=mM(i)*mM(i);
					sXM(i)=mX(i)*mM(i);
					sXY(i)=mX(i)*mY(i)+mCov(1,3)-mCov(1,2)*mCov(2,3)/mCov(2,2);
					sMY(i)=mM(i)*mY(i);
				}
			}else{
				if (matData(i,3)!=dmiss){
					mY(i)=matData(i,3);
					mX(i)=mCov(4,1)+mCov(1,3)/mCov(3,3)*(mY(i)-mCov(4,3));
					mM(i)=mCov(4,2)+mCov(2,3)/mCov(3,3)*(mY(i)-mCov(4,3));
					sX2(i)=mX(i)*mX(i)+mCov(1,1)-mCov(1,3)*mCov(1,3)/mCov(3,3);
					sY2(i)=mY(i)*mY(i);
					sM2(i)=mM(i)*mM(i)+mCov(2,2)-mCov(2,3)*mCov(2,3)/mCov(3,3);
					sXM(i)=mX(i)*mM(i)+mCov(1,2)-mCov(1,3)*mCov(2,3)/mCov(3,3);
					sXY(i)=mX(i)*mY(i);
					sMY(i)=mM(i)*mY(i);
				}
			}
		}
	}//end of the sample size for each pattern
	//Get the sum of each vector and put them into a matrix
	Matrix mSS(4,3);
	mSS(1,1)=sX2.sum();
	mSS(1,2)=sXM.sum();
	mSS(1,3)=sXY.sum();
	mSS(2,1)=mSS(1,2);
	mSS(2,2)=sM2.sum();
	mSS(2,3)=sMY.sum();
	mSS(3,1)=mSS(1,3);
	mSS(3,2)=mSS(2,3);
	mSS(3,3)=sY2.sum();
 
	mSS(4,1)=mX.sum();
	mSS(4,2)=mM.sum();
	mSS(4,3)=mY.sum();
 
	return mSS;
}
 
// Function to calcualte the difference of two matrix
double dfDiff(Matrix matCov1, Matrix matCov2){
	Matrix mDiff(4,3);
	double dError;
	int i,j;
	for (i=1; i<=4; i++){
		for (j=1; j<=3; j++){
			mDiff(i,j)=(matCov1(i,j)-matCov2(i,j))/matCov2(i,j);
		}
	}
	dError=mDiff.sum_absolute_value();
	return dError;
}
/*------------------------------------------------------*\ 
 * EM algorithm                                         *
\*------------------------------------------------------*/ 
RowVector cvfEM(Matrix matData, Matrix mCov, int iN){
	RowVector cvPara;
	double e=1.0;
	Matrix ssCov(4,3);
	Matrix mTCov(4,3);
	mTCov=mCov;
	Matrix mT2Cov(4,3);
 
	int iMaxI=10000;
	int j=1;
 
	while (e>0.0001 || j==iMaxI){
		ssCov=mfEstep(matData, mTCov, iN);
		mT2Cov=mfMstep(ssCov,iN);
		e=dfDiff(mT2Cov, mTCov);
		mTCov=mT2Cov;
		j++;
	}
	if (j==iMaxI){
		cout<<"\nThe EM algorithm cannot converge..."<<endl;
		exit(1);
	}
	cvPara=cvfParas(mTCov);
 
	return cvPara;
}
 
/*------------------------------------------------------*\ 
 * Delete a row from a matrix                           *
\*------------------------------------------------------*/ 
Matrix mDelARow(Matrix matData, int iR, int iN){
	Matrix mDel(iN-1, 3);
	if (iR==1){
		mDel=matData.submatrix(2,iN,1,3);
	}else if (iR==iN){
		mDel=matData.submatrix(1,iN-1,1,3);
	}else{
		mDel.submatrix(1,iR-1,1,3)=matData.submatrix(1,iR-1,1,3);
		mDel.submatrix(iR, iN-1,1,3)=matData.submatrix(iR+1,iN,1,3);
	}
	return mDel;
} 
 
/*------------------------------------------------------*\ 
 * Calculate the acceleration factor using Jackknife    *
\*------------------------------------------------------*/ 
RowVector rvJackA(Matrix matData, Matrix mCov,int iN){
	Matrix mJackPara(iN, 8), mDel(iN-1,3), mTemp1(iN,8), mTemp2(iN,8);
	RowVector dA(8), dMean(8), dSum1(8), dSum2(8);
	int i,j;
	for (i=1;i<=iN;i++){
		mDel = mDelARow(matData,i,iN);
		mJackPara.row(i)=cvfEM(mDel,mCov,iN-1);
	}
 
	dMean = mJackPara.sum_columns()/iN;
 
	for (i=1; i<=iN; i++){
		for (j=1; j<=8;j++){
			mTemp1(i,j) = (dMean(j)-mJackPara(i,j))*(dMean(j)-mJackPara(i,j))*(dMean(j)-mJackPara(i,j));
			mTemp2(i,j) = (dMean(j)-mJackPara(i,j))*(dMean(j)-mJackPara(i,j));
		}
	}
 
	dSum1=mTemp1.sum_columns();
	dSum2=mTemp2.sum_columns();
	for (i=1; i<=8; i++){
		dA(i)=dSum1(i)/(pow(dSum2(i),1.5)*6.0);
	}
	return dA;
}
/*------------------------------------------------------*\ 
 * Function to bootstrap                                *
\*------------------------------------------------------*/
Matrix mfBootOnce(Matrix matData, int iRow){
	Matrix mBoot(iRow, 3);
 
	Uniform U;
	int intvalue;
 
    for (int i=1; i<=iRow; i++){
		intvalue=int(U.Next()*iRow)+1;
		mBoot.row(i)=matData.row(intvalue);
	}
	return mBoot;
}
 
/*------------------------------------------------------*\ 
 * The stratified bootstrap                             *
\*------------------------------------------------------*/
Matrix mfBootstrap(Matrix matData, ColumnVector cvN, int iN){
 
	Matrix mStratBoot(iN,3);
	int n1=(int)cvN(1);
	int n2=(int)cvN(2);
	int n3=(int)cvN(3);
	int n4=(int)cvN(4);
	int n5=(int)cvN(5);
	int n6=(int)cvN(6);
	int n7=(int)cvN(7);
	int m2=n1+n2;
	int m3=m2+n3;
	int m4=m3+n4;
	int m5=m4+n5;
	int m6=m5+n6;
	int m7=m6+n7;
 
	if (n1>0){
		mStratBoot.submatrix(1,n1,1,3)=mfBootOnce(matData.submatrix(1,n1,1,3),n1);
	}
	if (n2>0){
		mStratBoot.submatrix(n1+1,m2,1,3)=mfBootOnce(matData.submatrix(n1+1,m2,1,3),n2);
	}
	if (n3>0){
		mStratBoot.submatrix(m2+1,m3,1,3)=mfBootOnce(matData.submatrix(m2+1,m3,1,3),n3);
	}
	if (n4>0){
		mStratBoot.submatrix(m3+1,m4,1,3)=mfBootOnce(matData.submatrix(m3+1,m4,1,3),n4);
	}
	if (n5>0){
		mStratBoot.submatrix(m4+1,m5,1,3)=mfBootOnce(matData.submatrix(m4+1,m5,1,3),n5);
	}
	if (n6>0){
		mStratBoot.submatrix(m5+1,m6,1,3)=mfBootOnce(matData.submatrix(m5+1,m6,1,3),n6);
	}
	if (n7>0){
		mStratBoot.submatrix(m6+1,m7,1,3)=mfBootOnce(matData.submatrix(m6+1,m7,1,3),n7);
	}
 
	return mStratBoot;
}
 
/*------------------------------------------------------*\ 
 * Function to calculate the CI for one parameter       *
\*------------------------------------------------------*/
RowVector rvfCI(ColumnVector rvBootRes, double dThetaHat, double dA, int iB, double alpha){
	RowVector rvCI(6);
	int i, iUp, iLow;
	double iPos=0.0;
	double tt1, tt2;
 
	if (alpha < 1.0-alpha){
		alpha = 1.0-alpha;
	}
 
	sort_ascending(rvBootRes);
 
	for (i=1; i<=iB; i++){
		if (rvBootRes(i)<dThetaHat){
			iPos=iPos+1.0;
		}
	}
	iLow = int((1.0-alpha)/2.0*iB+.5);
	rvCI(1)=rvBootRes(iLow);
	iUp = int((1.0+alpha)/2.0*iB+.5);
	rvCI(2)=rvBootRes(iUp);
 
	double dZ01=dfQNorm(iPos/iB);
	double zalpha1=dfQNorm((1.0-alpha)/2.0);
	double zalpha2=dfQNorm((1.0+alpha)/2.0);
 
	//CI for BC
	tt1=dfPNorm(2.0*dZ01+zalpha1);
	iLow = int(tt1*iB+.5);
	rvCI(3)=rvBootRes(iLow);
	tt2=dfPNorm(2.0*dZ01+zalpha2);
	iUp = int(tt2*iB+.5);
	rvCI(4)=rvBootRes(iUp);
 
	//CI for BCa
 
	tt1 = dfPNorm(dZ01+(dZ01+zalpha1)/(1-dA*(dZ01+zalpha1)));
	iLow = int(tt1*iB+.5);
	rvCI(5)=rvBootRes(iLow);
 
	tt2 = dfPNorm(dZ01+(dZ01+zalpha2)/(1-dA*(dZ01+zalpha2)));
    iUp = int(tt2*iB+.5);
	rvCI(6)=rvBootRes(iUp);
 
	return rvCI;
}
/*------------------------------------------------------*\ 
 * Function to calculate the CIs                        *
\*------------------------------------------------------*/ 
Matrix mBootCI(Matrix matData, Matrix mCov, ColumnVector cvN, int iN, int iB,double alpha){
	RowVector rvTrue=cvfEM(matData,mCov,iN);
	RowVector rvTemp(6), tempres(8);
	Matrix mBootData(iN,3),mBootRes(iB,8), mCIres(8,7);
    int k=int(iB/10);
	int i,j=1;
	cout<<"\nRunning: ";
 
 
	for (i=1; i<=iB; i++){
		mBootData=mfBootstrap(matData,cvN,iN);
		tempres=cvfEM(mBootData,mCov,iN);
		mBootRes.row(i)=tempres;
		if (i==j*k){
			cout<<j*10<<"% ";
			j++;
		}
	}
 
	//Run Jackknife
    cout<<"Finishing ";
    RowVector rvA=rvJackA(matData,mCov,iN);
	cout<<"DONE"<<"\n"; 
 
	for (i=1; i<=8; i++){
		rvTemp = rvfCI(mBootRes.column(i),rvTrue(i),rvA(i),iB,alpha);
		mCIres(i,1)=rvTrue(i);
		mCIres(i,2)=rvTemp(1);
		mCIres(i,3)=rvTemp(2);	
		mCIres(i,4)=rvTemp(3);
		mCIres(i,5)=rvTemp(4);
		mCIres(i,6)=rvTemp(5);
		mCIres(i,7)=rvTemp(6);
	}
 
	return mCIres;
}
 
 
/*------------------------------------------------------*\ 
 * The main program                                     *
\*------------------------------------------------------*/ 
int main()
{
// Print the program information
    cout <<"\n";
	cout <<"\n";
	cout <<"---------------------------------------------------------"<<endl;
	cout <<"| Program name: BootMedEM.exe (V1.0)                    |"<<endl;
	cout <<"| By Zhiyong Zhang & Lijuan Wang                        |"<<endl;
	cout <<"| Email zzhang4@nd.edu for questions and comments       |"<<endl;
	cout <<"| See readme.txt for more information                   |"<<endl;
	cout <<"---------------------------------------------------------"<<endl;
	cout <<"\n";
	cout <<"\n";
 
	cout <<"*********************************************************"<<endl;
	cout <<"*                       WARNING!                        *"<<endl;
	cout <<"* Although this program has been tested to perform as   *"<<endl;
	cout <<"* expected in many cases, we cannot guarantee its       *"<<endl;
	cout <<"* performance under all possible circumstances.         *"<<endl;
	cout <<"* YOU CAN USE IT FOR FREE BUT AT YOUR OWN RISK.         *"<<endl;
	cout <<"*********************************************************"<<endl;
	cout <<endl<<endl;
 
// Ask the input of the output file
	cout <<"Please input the output file name: "<<endl;
	std::string output;
	std::cin >>output;
	char *fout;
	fout=(char*)output.c_str();
	std::ofstream out;
 
	out.open(fout);
 
// Print out the file information	
	out <<"----------------------------------------------------------\n";
	out <<"| Program name: BootMedEM.exe (V1.0)                     |\n";
	out <<"| By Zhiyong Zhang & Lijuan Wang                         |\n";
	out <<"| Email zzhang4@nd.edu for questions and comments        |\n";
	out <<"| See readme.txt for more information                    |\n";
	out <<"----------------------------------------------------------\n";
	out <<"\n";
	out <<"\n";
	out <<"**********************************************************\n";
	out <<"*                       WARNING!                         *\n";
	out <<"* Although this program has been tested to perform as    *\n";
	out <<"* expected in many cases, we cannot guarantee its        *\n";
	out <<"* performance under all possible circumstances.          *\n";
	out <<"* YOU CAN USE IT FOR FREE BUT AT YOUR OWN RISK.          *\n";
	out <<"**********************************************************\n";
	out <<"\n\n";
 
	//time
	time_t rawtime;
	time(&rawtime);
    out <<"The BootMed program is ran on "<<ctime(&rawtime)<<"\n";
 
	out << "The output file is: ";
	out << output << "\n";
 
//Input the data file name and check the data	
	int c1;
	int r1;
	std::string filename;
    cout <<"\nPlease input the file name of the data:"<<endl;
	out <<"The input the file of the data is: ";
    std::cin >> filename;
	out << filename <<"\n";
	char *fstr;
    fstr= (char*)filename.c_str();
	size(fstr,r1, c1);
    //Decide if number of variables > 3
	if (c1>3){
		cout<<"The number of variables >3 !";
		cout<<"Only three varialbes (Y, X, Mediation) are allowed in the data file!";
		out<<"The number of variables >3 !\n";
		out<<"Only three varialbes (Y, X, Mediation) are allowed in the data file!";
		exit(1);
	}
 
 
//Read the data into a matrix
	Matrix INPORG=readdata(fstr,r1,c1);
 
 
 
	//request the input of the alpha level
 
	std::string stralpha;
 
	cout <<"\nPlease specify the alpha level (a number between 0 and 1 only):"<<endl;
	out <<"The alpha level is: ";
 
	std::cin >>stralpha;
 
    double alpha= atof(stralpha.c_str());
	//cout <<alpha <<endl;
	out <<alpha<<"\n";
	if (alpha>=1 || alpha<=0){
		cout << "Only a number bigger than 0 and less than 1 is accepted for the seed!"<<endl;
		out <<"Only a number bigger than 0 and less than 1 is accepted for the seed!\n";
		exit(1);
	}
 
 
	//request the input of seed for random number generation
 
	std::string strseed;
 
	cout <<"\nPlease specify the random number seed (a number between 0 and 1 only):"<<endl;
	out <<"The random number seed is: ";
	std::cin >>strseed;
	out <<strseed<<"\n";
 
	double seed= atof(strseed.c_str());
	//cout <<seed;
 
	if (seed>=1 || seed<=0){
		cout << "Only a number bigger than 0 and less than 1 is accepted for the seed!"<<endl;
		out <<"Only a number bigger than 0 and less than 1 is accepted for the seed!\n";
		exit(1);
	}
	MotherOfAll urng(seed);                    // declare uniform random number generator
    Random::Set(urng); 
 
	//Calculate the patterns of missing data
    ColumnVector cvN(8);
    Matrix matPatData(r1,c1);
    pattern(INPORG,r1,cvN,matPatData);
 
	//output the missing data pattern
	int iP=1;
	out <<"\n";
   	out <<"--------------------------------"<<"\n";
	out <<"|     Missing Data Pattern     |"<<"\n";
	out <<"--------------------------------"<<"\n";
	out <<"\n";
 
	out.width(7);out <<"Pattern"; out.width(3);out<<"X";
	out.width(3);out<<"M"; out.width(3);out<<"Y"<<"\n";
 
	if (cvN(1)>0){
		out.width(1);out<<iP;out.width(16);out<<"  o  o  o\n";
		iP++;
	}
	if (cvN(2)>0){
		out.width(1);out<<iP;out.width(16);out<<"  o  o  x\n";
		iP++;
	}
	if (cvN(3)>0){
		out.width(1);out<<iP;out.width(16);out<<"  o  x  o\n";
		iP++;
	}
	if (cvN(4)>0){
		out.width(1);out<<iP;out.width(16);out<<"  o  x  x\n";
		iP++;
	}
	if (cvN(5)>0){
		out.width(1);out<<iP;out.width(16);out<<"  x  o  o\n";
		iP++;
	}
	if (cvN(6)>0){
		out.width(1);out<<iP;out.width(16);out<<"  x  o  x\n";
		iP++;
	}
	if (cvN(7)>0){
		out.width(1);out<<iP;out.width(16);out<<"  x  x  o\n";
		iP++;
	}
	if (cvN(8)>0){
		out.width(1);out<<iP;out.width(16);out<<"  x  x  x\n";
	}
 
	out<<"o: observed; x: missing\n\n";
 
	if (cvN(1)==0){
		cout <<"Sorry, you asked too much for missing data analysis! There is no single case with complete data!"<<endl;
		out <<"Sorry, you asked too much for missing data analysis!\n There is no single case with complete data!\n"<<endl;
		exit(1);
	}
 
	if (cvN(8)>0){
		cout <<"What do you expect if no data observed for all three variables?"<<endl;
		out <<"What do you expect if no data observed for all three variables?\n"<<endl;
		exit(1);
	}
 
	std::string strBoot;
 
	cout <<"\nPlease input the bootstrap sample size:"<<endl;
	out <<"The bootstrap sample size is: ";
	std::cin>>strBoot;
 
    int iBoot=atoi(strBoot.c_str());
	out << strBoot<<"\n";
 
	//Running time
	clock_t tStTime, tEndTime;
	tStTime=clock();
 
	//cout<<matPatData;
	int iSize=int(cvN(1));
	Matrix mCov=mfCov(matPatData.submatrix(1,iSize,1,3),iSize,3);
 
	Matrix mBootStat=mBootCI(matPatData,mCov,cvN,r1,iBoot,alpha);
 
	//end time
	tEndTime=clock();
 
	//Output the missing data pattern
 
 
	out <<"\n\n";
   	out <<"--------------------------------------------------------"<<"\n";
	out <<"|     Estimated parameters and confidence interval     |"<<"\n";
	out <<"--------------------------------------------------------"<<"\n";
	out <<"\n";
 
	out.width(35); out<<"Quantile"; out.width(18); out<<"BC"; out.width(20); out<<"BCa";out<<"\n";
	out.width(10); out<<"Parameter"; out.width(10);out<<"Estimate"; out.width(8);out<<"L";out.width(10);out<<"U"; out.width(10);out<<"L";
	out.width(10);out<<"U";out.width(10);out<<"L";out.width(10);out<<"U";out<<"\n";
 
	int i;
 
    out << setiosflags(ios::fixed | ios::showpoint)<< setprecision(4);
    out.width(10); out << "iM";
    for (i=1; i<=7; i++){
	   out.width(10);  out<<mBootStat(1,i);
    }
    out<<"\n";
 
    // output results for intercept of Y
    out.width(10); out << "iY"; 
    for (i=1; i<=7; i++){
	   out.width(10); out<<mBootStat(2,i);
    }
   out<<"\n";
 
   // output results for a
   out.width(10); out << "a"; 
   for (i=1; i<=7; i++){
	   out.width(10);  out<<mBootStat(3,i);
   }
   out<<"\n";
 
      // output results for b
   out.width(10); out << "b"; 
   for (i=1; i<=7; i++){
	   out.width(10);  out<<mBootStat(4,i);
   }
   out<<"\n";
 
   // output results for c
   out.width(10); out << "c"; 
   for (i=1; i<=7; i++){
	   out.width(10); out<<mBootStat(5,i);
   }
   out<<"\n";
 
   // output results for eM2
   out.width(10); out << "eM2"; 
   for (i=1; i<=7; i++){
	   out.width(10); out<<mBootStat(6,i);
   }
   out<<"\n";
 
   // output results for residual errors of Y
   out.width(10); out << "eY2";
   for (i=1; i<=7; i++){
	   out.width(10); out<<mBootStat(7,i);
   }
   out<<"\n";
 
      // output results for sig1
   out.width(10); out << "a*b"; 
   for (i=1; i<=7; i++){
	   out.width(10);  out<<mBootStat(8,i);
   }
    //timer
   	out <<"\n\nThe total running time is "<<(double)(tEndTime-tStTime)/CLOCKS_PER_SEC<<" seconds. \n";
 
   cout<<"\nThe parameter estimates and the confidence intervals are:"<<endl;
   cout<<"Please see the output file [["<<output<<"]] for complete results."<<endl;
   cout << setiosflags(ios::fixed | ios::showpoint)<< setprecision(4);
   cout<<setw(10)<<mBootStat;
 
   out.close();
 
   return 0;
 
}