#include "StdAfx.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <mpi.h>

FILE *f_input, *f_matrix, *f_vector, *f_res, *f_time;

int procQuantity;	
int procRank;	

int* pProcInd; 
int* pProcNum; 

void ProcessInitialization (double* &pVector, double* &pResult, double* &pProcRows, 
							double* &pProcVector, double* &pProcResult, int &Size, int &RowNum) 
{
	int i,j;       
	MPI_Status Status;

	if (procRank == 0)
	{
		f_input = fopen("C:\\Users\\main\\Documents\\Visual Studio 2010\\Projects\\lab1\\Debug\\Systems\\system1000.txt", "r");
		fscanf(f_input, "%d\n", &Size);
		f_vector = fopen("C:\\Users\\main\\Documents\\Visual Studio 2010\\Projects\\lab1\\Debug\\Systems\\vector1000.txt", "r");
		fscanf(f_vector, "%d\n", &Size);
	}

	MPI_Bcast(&Size, 1, MPI_INT, 0, MPI_COMM_WORLD);

	int missingRows = Size;
	RowNum = Size/procQuantity;
	bool fl = false;
	missingRows = Size - RowNum * procQuantity;
	j = 0;
	if (missingRows != 0)
	{
		for (i = 0; i < procRank; i++) 
		{
			missingRows--;
			if (missingRows == 0)
			{
				fl = true;
				j = i;
				break;
			}
		}
		if (fl == false)
			RowNum++;
	}
	MPI_Bcast(&j, 1, MPI_INT, procQuantity-1, MPI_COMM_WORLD);
	printf("%d \n", j);
	pProcRows = (double*)malloc(RowNum*Size*sizeof(double));

	pProcVector = new double [RowNum];
	pProcResult = new double [RowNum];   
  
	pProcInd = new int [procQuantity];   
	pProcNum = new int [procQuantity];  

	if (procRank == 0) 
	{
		pVector = new double [Size];
		pResult = new double [Size];

		for (i=0; i<Size; i++)
		{
			fscanf(f_vector, "%lg",pVector + i);
		}
		fclose(f_vector);
	}


	pProcInd[0] = 0;
	pProcNum[0] = ceil((float)Size/procQuantity);
	missingRows = Size/procQuantity;

	for (i=1; i < procQuantity; i++) 
	{
		if (i <= j)
			pProcNum[i] = pProcNum[0];
		else
			pProcNum[i] = missingRows;
		pProcInd[i] = pProcInd[i-1]+pProcNum[i-1];
	}

	if (procRank == 0)
	{
		int k = 0;
		for (i = 0; i < Size; i++)
		{
			int destRank = i % procQuantity;
		
			if (destRank == 0)
			{
				for (j = 0; j < Size; j++)
					fscanf(f_input, "%lg", &pProcRows[(i / procQuantity) * Size+ j]);
				pProcVector[k++] = pVector[i];
			}
			else
			{
				for (j = 0; j < Size; j++)
					fscanf(f_input, "%lg", &pResult[j]);
				MPI_Send(pResult, Size, MPI_DOUBLE, destRank, i / procQuantity, MPI_COMM_WORLD);
				MPI_Send(&pVector[i], 1, MPI_DOUBLE, destRank, Size+1, MPI_COMM_WORLD);
			}
		}

		fclose(f_input);
	}
	else
	{
		for (i = 0; i  < pProcNum[procRank]; i++)
		{
			MPI_Recv(&pProcRows[i*Size], Size, MPI_DOUBLE, 0, i, MPI_COMM_WORLD, &Status);
			MPI_Recv(&pProcVector[i],1, MPI_DOUBLE, 0, Size+1, MPI_COMM_WORLD, &Status);
		}
	}
	printf("%d %d\n", procRank, pProcNum[procRank]);
}

void ColumnElimination(double* pProcRows, double* pProcVector, double* pRow, int Size, int RowNum, int Iter, int currentRowIndex) 
{
	double multiplier; 

	for (int i=currentRowIndex; i<RowNum; i++) 
	{
		multiplier = pProcRows[i*Size+Iter] / pRow[Iter]; 

		for (int j=Iter; j<Size; j++) 
			pProcRows[i*Size + j] -= pRow[j]*multiplier;
		pProcVector[i] -= pRow[Size]*multiplier;
	}    
}

void GaussianElimination (double* pProcRows, double* pProcVector, int Size, int RowNum)
{
	double* castedRow = new double [Size+1];
	int pRowIndex, currentRank;
	pRowIndex = 0;
	currentRank = 0;
	for (int i = 0; i < Size; i++)  
	{  
		if (procRank == currentRank)
		{
			for (int j=0; j<Size; j++) 
			{
				castedRow[j] = pProcRows[pRowIndex*Size + j];
			}
			castedRow[Size] = pProcVector[pRowIndex];
			pRowIndex++;
		}

		MPI_Bcast(castedRow, Size+1, MPI_DOUBLE, currentRank, MPI_COMM_WORLD);

		ColumnElimination(pProcRows, pProcVector, castedRow, Size, RowNum, i, pRowIndex);

		currentRank++;
		if (currentRank == procQuantity)
			currentRank = 0;
	}
}

void BackSubstitution (double* pProcRows, double* pProcVector, double* pProcResult, int Size, int RowNum) 
{
	double IterResult;   
	double val;
	int pRowIndex, currentRank;
	pRowIndex = RowNum - 1;
	currentRank = (Size - 1) % procQuantity;
	for (int i = Size-1; i >= 0; i--) 
	{
		if (procRank == currentRank) 
		{
			IterResult = pProcVector[pRowIndex]/pProcRows[pRowIndex*Size+i];
			pProcResult[pRowIndex] = IterResult;
			pRowIndex--;
		}
		MPI_Bcast(&IterResult, 1, MPI_DOUBLE, currentRank, MPI_COMM_WORLD);

		for (int j = 0; j < pRowIndex + 1; j++) 
		{
			val = pProcRows[j*Size + i] * IterResult;
			pProcVector[j] = pProcVector[j] - val;
		}
		currentRank--;
		if (currentRank < 0)
			currentRank = procQuantity - 1;
	}
}

void ProcessTermination (double* pVector, double* pResult, double* pProcRows, double* pProcVector, double* pProcResult) 
{
	if (procRank == 0) 
	{
		delete [] pVector;
		delete [] pResult;
	}

	free(pProcRows);
	delete [] pProcVector;
	delete [] pProcResult;

	delete [] pProcInd;
	delete [] pProcNum;
}

int main(int argc, char* argv[]) 
{
	double* pVector; 
	double* pResult;  
	int	  Size;     
  
	double *pProcRows;      
	double *pProcVector;   
	double *pProcResult;    
	int     RowNum;         

	double  start, finish, duration; 

	MPI_Init ( &argc, &argv );
	MPI_Comm_rank ( MPI_COMM_WORLD, &procRank );
	MPI_Comm_size ( MPI_COMM_WORLD, &procQuantity );
  
	ProcessInitialization(pVector, pResult, pProcRows, pProcVector, pProcResult, Size, RowNum);

	start = MPI_Wtime();

	//GaussianElimination(pProcRows, pProcVector, Size, RowNum);

 
	//BackSubstitution (pProcRows, pProcVector, pProcResult, Size, RowNum);
 
	//MPI_Gatherv(pProcResult, pProcNum[procRank], MPI_DOUBLE, pResult, pProcNum, pProcInd, MPI_DOUBLE, 0, MPI_COMM_WORLD);

	finish = MPI_Wtime();
	duration = finish-start;
  
	//if (procRank == 0) 
	//{
	//	f_res = fopen("C:\\Users\\main\\Documents\\Visual Studio 2010\\Projects\\lab1\\Debug\\Solutions\\result.txt", "w");
	//	int k = 0;
	//	int j =0;
	//	for (int i=0; i<Size; i++)
	//	{
	//		fprintf(f_res,"%7.4f ", pResult[k]);
	//		k += RowNum;
	//		if (k >= Size)
	//		{
	//			j++;
	//			k = j;
	//		}
	//	}
	//	fclose(f_res);

	//	f_time = fopen("C:\\Users\\main\\Documents\\Visual Studio 2010\\Projects\\lab1\\Debug\\Solutions\\Time.txt", "a+");
	//	fprintf(f_time, " Number of processors: %d\n Size of Matrix: %d\n Time of execution: %f\n\n", procQuantity, Size, duration);
	//	fclose(f_time);
	//}

	ProcessTermination(pVector, pResult, pProcRows, pProcVector, pProcResult);
	MPI_Finalize();
	return 0;
}