#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>

unsigned long*Table,*ColSum,*RowSum,LowBound,UpBound,*TempTable;
unsigned long CumColSum[2],CumRowSum[2];
unsigned long TTable[2][2],TColSum[2],TRowSum[2];
unsigned long Sum,temp,TSum,Size,Total;
int TCol,TRow,Ind_C,Ind_R,Col,Row;
double CHstats,Hstats,temp1;
double Mvol,Hvol,Dvol,CHstats,Hstats;
double Mvol_size,Hvol_size,Dvol_size;
double Weight;

unsigned long generate()
 {
  unsigned long int i;
  unsigned long int intu;

  intu=(65539*rand())%Total;
  for(i=0;i<1;i++)
  { 
    intu=(65539*intu)%Total;   
  }
  return (unsigned long)((double)intu/Total*(UpBound-LowBound+1)+LowBound);
}

unsigned long Max(long x, long y)
{
	if(x<y)return (unsigned long)y;
	else return (unsigned long)x;
}

/*Return the maximum value*/
unsigned long Min(unsigned long x,unsigned long y)
{
	if(x<y)return x;
	else return y;
}

//Evaluate the chi-square statistics
double chisq(unsigned long*table)
{   
	int i,j;
	double chis2=0,tt;
	for(i=0;i<Row;i++)
		for(j=0;j<Col;j++)
		{
	        tt=table[i*Col+j]-(double)RowSum[i]*ColSum[j]/(double)Sum;
			chis2+=tt*tt*(double)Sum/(RowSum[i]*ColSum[j]);
		}
	return chis2;
}

/*Sign function*/
int sign(double x)
{
	if(x>0)return 1;
	else return -1;
}

/*Excess of homozygosity*/
double H_T(unsigned long*table)
{
	int i,j;
	double ht=0,ht1=0,ht2=0;
	for(i=0;i<Col;i++)
		ht2+=ColSum[i]*ColSum[i];
	for(i=0;i<Row;i++)
	{
        ht1+=RowSum[i]*RowSum[i];		
		for(j=0;j<Col;j++)
		{
			ht+=table[i*Col+j]*table[i*Col+j];
		}
	}
	return ht-ht1*ht2/Sum/Sum;
}

//Build up the tree and Evaluate the statistics
void FillIn()
{ 
  	   if(TCol<Col-1&&TRow<Row-1)
	   {
		   if(TRow==0)
		   {
			   CumColSum[0]=0;
			   CumColSum[1]=0;
		   }
		   else
		   {
		     for(Ind_R=0;Ind_R<TRow;Ind_R++)
			 {
			   if(Ind_R==0){CumColSum[0]=TempTable[TCol];}
			   else{CumColSum[0]+=TempTable[Ind_R*Col+TCol];}
			   temp=0;
			   for(Ind_C=0;Ind_C<=TCol-1;Ind_C++)
				   temp+=TempTable[Ind_R*Col+Ind_C];
			   if(Ind_R==0)CumColSum[1]=temp;
			   else CumColSum[1]+=temp;
			   if(Ind_R==0)TSum=RowSum[Ind_R];
			   else TSum+=RowSum[Ind_R];
			 }
		     CumColSum[1]=TSum-CumColSum[1];
		   }

		   for(Ind_C=TCol;Ind_C<Col;Ind_C++)
		   {
               if(Ind_C==TCol) TColSum[1]=ColSum[Ind_C];
			   else TColSum[1]+=ColSum[Ind_C];
		   }
		   TSum=TColSum[1]-CumColSum[1];
		   if(TCol==0)
		   {
			   CumRowSum[0]=0;
		   }
		   else
		   {
		       for(Ind_C=0;Ind_C<TCol;Ind_C++)
			   {
			      if(Ind_C==0){CumRowSum[0]=TempTable[TRow*Col];}
			      else{CumRowSum[0]+=TempTable[TRow*Col+Ind_C];}

			   }
		   }
           TColSum[0]=ColSum[TCol]-CumColSum[0];
		   TRowSum[0]=RowSum[TRow]-CumRowSum[0];
		   TColSum[1]=TSum-TColSum[0];
		   TRowSum[1]=TSum-TRowSum[0];
           LowBound=Max(0,TColSum[0]-TRowSum[1]);
           UpBound=Min(TColSum[0],TRowSum[0]);
		   Weight=Weight*(UpBound-LowBound+1);
	       TempTable[TRow*Col+TCol]=generate();

	   }
	   else if(TRow<Row-1)
	   {
		   temp=0;
		   for(Ind_C=0;Ind_C<TCol;Ind_C++)
		   {
              temp+=TempTable[TRow*Col+Ind_C];
		   }
		   LowBound=UpBound=RowSum[TRow]-temp;
		   TempTable[TRow*Col+TCol]=LowBound;
	   }
	   else 
	   {
		   temp=0;
		   for(Ind_R=0;Ind_R<TRow;Ind_R++)
		   {
			   temp+=TempTable[Ind_R*Col+TCol];
		   }
		   LowBound=UpBound=ColSum[TCol]-temp;
		   TempTable[TRow*Col+TCol]=LowBound;
		   if((TRow==Row-1)&&(TCol==Col-1))
		   {
	         temp1=chisq(&TempTable[0]);

		/*M-Volumn Test*/
		     Mvol_size+=Weight;
             if(temp1<CHstats)
			   Mvol+=Weight;

		/*D-Volumn Test*/
		     if((Table[0]-ColSum[0]*RowSum[0]/(double)Sum)*(TempTable[0]-ColSum[0]*RowSum[0]/(double)Sum)>=0)
			 {
			    Dvol_size+=Weight;
			    if(temp1<CHstats)
				Dvol+=Weight;
			 }

		     /*H-Volumn Test*/
             temp1=H_T(&TempTable[0]);
		     if(temp1*Hstats>=0)
			 {
			    Hvol_size+=Weight;
			    if(sign(temp1)*temp1<sign(Hstats)*Hstats)
				  Hvol+=Weight;
			 }
		   }
	   }	 
}

void evaluate()
{

	int i,j,k,Limit;
	unsigned long l;

    /*Evaluate the Chi-square statistic of observed table*/
	CHstats=chisq(&Table[0]);

	/*Evaluate the H statistics of observed table*/
	Hstats=H_T(&Table[0]);
	srand((unsigned)time(NULL));
	Limit=Min(Row-1,Col-1);
	for(l=0;l<Size;l++)
	{

	  for(k=0;k<Limit;k++)
	  {
		for(i=k;i<Col;i++)
		{
			if(i==0&&k==0)
			{
				TRowSum[0]=RowSum[0];
	            TColSum[0]=ColSum[0];
	            TRowSum[1]=Sum-RowSum[0];
	            TColSum[1]=Sum-ColSum[0];
                LowBound=Max(0,TColSum[0]-TRowSum[1]);
                UpBound=Min(TColSum[0],TRowSum[0]);
				TRow=0;
				TCol=0;
				Weight=1;
	            FillIn();
			}
			else
			{
			    TRow=k;
			    TCol=i;
			    FillIn();
			}
		}
		for(j=k+1;j<Row;j++)
		{
			TRow=j;
			TCol=k;
			FillIn();
		}
	  }
	  if(Row<Col)
	  {
		  TRow=Row-1;
		  for(i=Row-1;i<Col;i++)
		  {
			  TCol=i;
			  FillIn();
		  }
	  }
	  else
	  {
		  TCol=Col-1;
		  for(i=Col-1;i<Row;i++)
		  {
			  TRow=i;
			  FillIn();
		  }
	  }
	}
	Mvol=Mvol/Mvol_size;
	Dvol=Dvol/Dvol_size;
	Hvol=sign(Hstats)*Hvol/Hvol_size;
}

main()
{
	FILE*in,*out;
	char Input[256],Output[256];
	int i,j,cell,length, flag;
	unsigned long control;

	/*Input the Table*/
	printf("\nPlease enter the data file name (including the folders,i.e. c:\\user\\table.txt):\n");
	scanf("%s",Input);
	printf("Please enter the output file name (including the folders,i.e. c:\\user\\output.txt):\n");
	scanf("%s",Output);
	while((in=fopen(Input,"r"))==NULL)
	{
	  printf("\nPlease enter the data file name (including the folders,i.e. c:\\user\\table.txt):\n");
	  scanf("%s",Input);
	}
	while((out=fopen(Output,"w"))==NULL)
	  {
	    printf("Please enter the output file name (including the folders,i.e. c:\\user\\output.txt):\n");
	    scanf("%s",Output);
	  }
	printf("\nHow many Monte Carlo samples?\n");
	scanf("%ld",&Size);
	fscanf(in,"%d %d %d",&Row,&Col, &cell);
	for(i=0;i<Row;i++)
	{
	   for(j=0;j<Col;j++)
	   {
			fscanf(in,"%ld",&control);
	   }
	}
	length=0;
	do
	{
		fscanf(in,"%ld",&control);
		if(control==0)
			length++;
	}while(control==0);
	length+=Row*Col;
	rewind(in);
	fprintf(out,"%s","Mvol     Hvol\n");
	Total=1;
    for(i=0;i<31;i++)
	  Total=Total*2;
	while(!feof(in))
	{
		Mvol=Hvol=Dvol=0;
		Mvol_size=Hvol_size=Dvol_size=0;
		Sum=0;

		/*Enter the dimensions information of the matrix*/
		fscanf(in,"%d %d %d",&Row,&Col,&cell);
		Table=(unsigned long*)malloc(Row*Col*sizeof(unsigned long));
		TempTable=(unsigned long*)malloc(Row*Col*sizeof(unsigned long));
		ColSum=(unsigned long*)malloc(Col*sizeof(unsigned long));
		RowSum=(unsigned long*)malloc(Row*sizeof(unsigned long));

		/*Input the Table*/
	    for(i=0;i<2;i++)
		{
		   CumRowSum[i]=0;
		   CumColSum[i]=0;
		   for(j=0;j<2;j++)
			  TTable[i][j]=0;
		   TColSum[i]=0;
		   TRowSum[i]=0;
		}
	    for(i=0;i<Row;i++)
		{
		   for(j=0;j<Col;j++)
		   {
			   TempTable[i*Col+j]=0;
			   fscanf(in,"%ld",&Table[i*Col+j]);
			   Sum+=Table[i*Col+j];
			   if(j==0)RowSum[i]=Table[i*Col+j];
			   else RowSum[i]+=Table[i*Col+j];
			   if(i==0)ColSum[j]=Table[i*Col+j];
			   else ColSum[j]+=Table[i*Col+j];
		   }
		}
	    for(i=0;i<length-cell;i++)
	      fscanf(in,"%ld",&control);

	    flag=0;
	    for(i=0;i<Row;i++) {
	      if (RowSum[i] == 0) {
		flag=1;
		break;
	      }
	    }
	    if (flag == 0) {
	      for(j=0;j<Col;j++) {
		if (ColSum[j] == 0) {
		  flag=1;
		  break;
		}
	      }
	    }

	    if (flag == 1) {
	      printf("Error message: Not all row and column sums are 
positive. Please remove rows and columns with zero sums.\n");
	       break;
	    }          

	    evaluate();
	    fprintf(out,"%lf  %lf\n",Mvol,Hvol);

	    free(Table);
	    free(TempTable);
	    free(ColSum);
	    free(RowSum);
	    fscanf(in,"\n");
	}
	fclose(out);
	fclose(in);
}