/*
	"PI_M_C - example for SCS"
	(C) by Grigori G.Fursin
	1998

	              OO
	             ----
	             \       n        4
	Formula: PI=  |  (-1)  *  --------- 
	             /             2*n + 1
	             ----
	             n=0

	Algorithm is using MPI Library
*/

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <assert.h>
#include <mpi.h>

#define StrMaxLen 1024

#define MASTER 0 /* ID of master process */

int main(int argc, char **argv)
{
	char cfg[]="pi_m_c.cfg";
	char out[]="pi_m_c.out";
	
	FILE *fin;
	FILE *fout;

	char str[StrMaxLen];

	int t_loop;             /* Type of loop */
	unsigned long n_it;     /* Number of iterations */
	unsigned long n_it_p;   /* Period to print */
	int t_er;               /* If error ... */
	unsigned long n_er;     /* Number of iteration, when error */

	unsigned long i;
	unsigned long x;

	time_t time_start, time_finish;

	double time_diff;

	double pi_std = 3.1415926535897932;
	double pi_my;
	double pi_my1;
	double f1;

	int nproc;	/* number of processes */
	int id_my;	/* ID of my process*/
	int id;

	MPI_Status m_status;
	MPI_Request m_request;
	int m_type=0;

	/* Preparing */
	MPI_Init(&argc, &argv);
	MPI_Comm_size(MPI_COMM_WORLD, &nproc);
	MPI_Comm_rank(MPI_COMM_WORLD, &id_my);
	
	/* Print name of program */
	if (id_my==MASTER)
	{
		/* MASTER process */

		strcpy(str, "\"PI_M_C - example for SCS\"  (C) by Grigori G.Fursin  1998\n\n");
		printf(str);

		if ((fout=fopen(out, "w"))==NULL)
		{
			printf("ERROR: Can't open output file \"%s\"!\n",out);
			return 1;
		}
	
		fprintf(fout, str);

		if ((fin=fopen(cfg,"r"))==NULL)
		{
			strcpy(str, "ERROR: Can't find file with configuration \"%s\"!\n");
			printf(str, cfg);
			fprintf(fout, str, cfg);
			
			fclose(fout);

			return 1;
		}

		/* t_loop = 1 - Loop is INFINITE, 
					otherwise Loop is INFINITIE */
		if (fscanf(fin, "%i", &t_loop)==EOF)
		{
_un_end:
			strcpy(str, "ERROR: Unexpected end of file with configuration \"%s\"!\n");
			printf(str, cfg);
			fprintf(fout, str, cfg);

			fclose(fin);
			fclose(fout);

			return 1;
		}

		/* n_it = Number of iterations */
		if (fscanf(fin, "%i", &n_it)==EOF) goto _un_end;

		/* n_it_p = Period when print results */
		if (fscanf(fin, "%i", &n_it_p)==EOF) goto _un_end;

		/* t_er = 1 - If error will be generated, 
				otherwise error will not be generated */
		if (fscanf(fin, "%i", &t_er)==EOF) goto _un_end;
	
		/* If t_er = 1, n_er = number of iteration, 
							when error is generated */
		if (fscanf(fin, "%i", &n_er)==EOF) goto _un_end;
	
		fclose(fin);

		/* Checking */
		if (t_loop!=1) 
		{
			if (n_it<1) n_it=1;
			if (n_it_p<1) n_it_p=1;
		}

		/* Sending configuration parameters to all other processes */
		for (id=0; id<nproc; id++)
		{
			if (id!=MASTER) 
			{
				MPI_Isend(&t_loop, 1, MPI_INT, id, m_type, MPI_COMM_WORLD, &m_request);
				MPI_Wait(&m_request, &m_status);

				MPI_Isend(&n_it, 1, MPI_UNSIGNED_LONG, id, m_type, MPI_COMM_WORLD, &m_request);
				MPI_Wait(&m_request, &m_status);

				MPI_Isend(&n_it_p, 1, MPI_UNSIGNED_LONG, id, m_type, MPI_COMM_WORLD, &m_request);
				MPI_Wait(&m_request, &m_status);
			}
		}

		time(&time_start);
	
		/* Begin of algorithm */
		i=1;
		pi_my=4; /* When i=0 */
	
		strcpy(str,"   Iteration:                PI:             Error:\n\n");
		printf(str);
		fprintf(fout, str);
	
		pi_my1=0;
		
		do
		{
			x=(i-1)*nproc+id_my+1;
			if ((x%2)==0) f1=1; else f1=-1;

			pi_my+=4*f1*(1/(1+2*(double)x));
			
			if ((i%n_it_p)==0)
			{
				/* Receiving sums from other processes */
				for (id=0; id<nproc; id++)
				{
					if (id!=MASTER) 
					{
						MPI_Recv(&pi_my1, 1, MPI_DOUBLE, id, m_type, MPI_COMM_WORLD, &m_status);
						pi_my+=pi_my1;
					}
				}
				
				strcpy(str,"%12u) %2.16f %2.16f\n");
				printf(str, i, pi_my, fabs(pi_my-pi_std));
				fprintf(fout, str, i, pi_my, fabs(pi_my-pi_std));
			}
			i++;

			if ((t_er==1)&&(i==n_er))
			{
				assert(0!=0);

				/*
				i=0;
				pi_my=1/i;
				*/

				return 255;
			}
		}
		while ((i<=n_it)||(t_loop==1));
		/* End of algorithm */
	
		time(&time_finish);

		time_diff=difftime(time_finish, time_start);

		strcpy(str, "\nPI (from program) = %1.16f\n");
		printf(str, pi_my);
		fprintf(fout, str, pi_my);

		strcpy(str, "PI (standard)     = %1.16f\n");
		printf(str, pi_std);
		fprintf(fout, str, pi_std);

		strcpy(str, "Error             = %1.16f\n");
		printf(str, fabs(pi_my-pi_std));
		fprintf(fout, str, fabs(pi_my-pi_std));
	
		strcpy(str, "\nProgram takes %6.0f second(s).\n");
		printf(str, time_diff);
		fprintf(fout, str, time_diff);
	
		strcpy(str, "\nEnd of Program!\n");
		printf(str);
		fprintf(fout, str);

		fclose(fout);
	}
	else
	{
		/* SLAVE process */
		/* Receiving configuration parameters from MASTER */
		MPI_Recv(&t_loop, 1, MPI_INT, MASTER, m_type, MPI_COMM_WORLD, &m_status);
		MPI_Recv(&n_it, 1, MPI_UNSIGNED_LONG, MASTER, m_type, MPI_COMM_WORLD, &m_status);
		MPI_Recv(&n_it_p, 1, MPI_UNSIGNED_LONG, MASTER, m_type, MPI_COMM_WORLD, &m_status);

		/* Begin of algorithm */
		i=1;
		pi_my=0; /* When i=0 */
	
		do
		{
			x=(i-1)*nproc+id_my+1;
			if ((x%2)==0) f1=1; else f1=-1;

			pi_my+=4*f1*(1/(1+2*(double)x));
				
			if ((i%n_it_p)==0)
			{
				/* Sending sum to MASTER */
				MPI_Isend(&pi_my, 1, MPI_DOUBLE, MASTER, m_type, MPI_COMM_WORLD, &m_request);
				MPI_Wait(&m_request, &m_status);
				
				/* Clearing sum */
				pi_my=0;
			}
			i++;
		}
		while ((i<=n_it)||(t_loop==1));
		/* End of algorithm */
	}

	MPI_Finalize();
	
	return 0;
}
