//	"PI_M_CPP - 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 <iostream.h>
#include <fstream.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <assert.h>
#include <stdlib.h> // for atoi
#include <mpi.h>

#include "fggcfg.h"
CFGGCfg* fggcfg=NULL;
#define CfgMaxParam 5

#define StrMaxLen 1024

ofstream fout; // Output to cout & fout !!!

#define MASTER 0 // ID of master process

int main(int argc, char **argv)
{
	char cfg[]="pi_m_cpp.cfg";
	char out[]="pi_m_cpp.out";
	
	ifstream fin;

	int ret;

	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);

	if (id_my==MASTER)
	{
		// MASTER process

		cout.setf(ios::unitbuf);
	
		strcpy(str, "\"PI_CPP - example for SCS\"  (C) by Grigori G.Fursin  1998\n");
		cout << str;
	
		fout.open(out);
		if (!fout)
		{
			cout << "\nERROR: Can't open output file \"" << out << "\"!\n";
			return 1;
		}

		fout.setf(ios::unitbuf);
		fout << str;
	
		static CFGGCfg CSCS_CCfg(CfgMaxParam, StrMaxLen, cfg);

		fggcfg=&CSCS_CCfg;
		ret=CSCS_CCfg.Load();
		if (ret!=0)
		{
			switch (ret)
			{
			case 1:
				strcpy(str, "Can't find file with configuration \"");
				strcat(str, cfg);
				strcat(str, "\"");

				break;
			case 2:
				strcpy(str, "Line is too long in file with configuration \"");
				strcat(str, cfg);
				strcat(str, "\"");

				break;
			case 3:
				strcpy(str, "File with configuration \"");
				strcat(str, cfg);
				strcat(str, "\" is incomplete!");

				break;
			default:
				strcpy(str, "Can't load file with configuration \"");
				strcat(str, cfg);
				strcat(str, "\"");

				break;
			}

			cout << "ERROR:" << str << "!\n";
			fout << "ERROR:" << str << "!\n";

			fout.close();

			return 1;
		}
	
		// Reading configuration

		// t_loop = 1 - Loop is INFINITE, 
		//          otherwise Loop is INFINITIE
		t_loop=atoi(fggcfg->Get(1));
	
		// n_it = Number of iterations
		n_it=atol(fggcfg->Get(2));

		// n_it_p = Period when print results
		n_it_p=atol(fggcfg->Get(3));

		// t_er = 1 - If error will be generated, 
		//        otherwise error will not be generated 
		t_er=atoi(fggcfg->Get(4));
	
		// If t_er = 1, n_er = number of iteration, 
		//                     when error is generated
		n_er=atol(fggcfg->Get(5));
	
		// 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,"\n   Iteration:                PI:             Error:\n\n");
		cout << str;
		fout << str;
	
		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;
					}
				}

				cout.width(12);
				cout << i << ") ";
				cout.setf(ios::fixed);
				cout.width(18);
				cout.precision(16);
				cout << pi_my;
				cout << " ";
				cout.setf(ios::fixed);
				cout.width(18);
				cout.precision(16);
				cout << fabs(pi_my-pi_std);
				cout << "\n";

				fout.width(12);
				fout << i << ") ";
				fout.setf(ios::fixed);
				fout.width(18);
				fout.precision(16);
				fout << pi_my;
				fout << " ";
				fout.setf(ios::fixed);
				fout.width(18);
				fout.precision(16);
				fout << fabs(pi_my-pi_std);
				fout << "\n";
			}
			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) = ");
		cout << str;
		cout.setf(ios::fixed);
		cout.width(18);
		cout.precision(16);
		cout << pi_my << "\n";
		fout << str;
		fout.setf(ios::fixed);
		fout.width(18);
		fout.precision(16);
		fout << pi_my << "\n";
	
		strcpy(str, "PI (standard)     = ");
		cout << str;
		cout.setf(ios::fixed);
		cout.width(18);
		cout.precision(16);
		cout << pi_std << "\n";
		fout << str;
		fout.setf(ios::fixed);
		fout.width(18);
		fout.precision(16);
		fout << pi_std << "\n";

		strcpy(str, "Error             = ");
		cout << str;
		cout.setf(ios::fixed);
		cout.width(18);
		cout.precision(16);
		cout << fabs(pi_my-pi_std) << "\n";
		fout << str;
		fout.setf(ios::fixed);
		fout.width(18);
		fout.precision(16);
		fout << fabs(pi_my-pi_std) << "\n";
		
		strcpy(str, "\nProgram takes");
		cout << str;
		cout.setf(ios::fixed);
		cout.width(6);
		cout.precision(0);
		cout << time_diff << " second(s).\n";
		fout << str;
		fout.setf(ios::fixed);
		fout.width(6);
		fout.precision(0);
		fout << time_diff << " second(s).\n";
	
		strcpy(str, "\nEnd of Program!\n");
		cout << str;
		fout << str;

		fout.close();

		return 0;
	}
	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;
}
