#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>

/* Copyright 1997 Tommy Johnson All Rights Reserved.
**
** Permission is hereby granted to copy, reproduce, redistribute or otherwise
** use this software as long as: there is no monetary profit gained
** specifically from the use or reproduction of this software, it is not
** sold, rented, traded or otherwise marketed, and this copyright notice is
** included prominently in any copy made.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ANY USE OF THIS
** SOFTWARE IS AT THE USER'S OWN RISK.
*/    

#define MAXMESG 8192

/* #define DEBUG   */

/* message types sent to master from slaves */
#define DATA 1
#define HUNGRY 2
/* Format:  (messages to master from slaves, all are ints)
node
function    The defines above...
if function==DATA, the following is appended
row         value passed to slave from master
column      value passed to slave from master
len         number of pixels in this message

Note the MAXMESG define above, which limits the length of
a message.  the slave will break up a region if it dosn't
fit.  
*/

/* message types send to slaves from master */
#define COMPUTE 1
#define DONE 2
#define POKE 3
/* Format:   (messages to slaves from master, all are doubles)
node		node number, mostly to pass back to master
function        What to do, value is the defines above
  When function==COMPUTE the following is appended:
x               real coord
y               imaginary coord
skip            space between pixels, in real coord units
count           number of pixels
maxint          max iteration
row             row of this fragment, to pass back to master
col             column of this fragment, to pass back to master

What happens:
All the processes startup, and hit a barrier, until all have
started.  Then, process 0 becomes master, and all the others
become slaves.

The master waits for a command on stdin.  When the command is
recieved, it sends POKE messages to all the slaves, then enters
a while loop.

When the master recieves a HUNGRY message, it sends the slave it
got it from a COMPUTE message, for a region, and marks the region
"in progress".  When the master recieves a DATA message, it forwards
it out stdout, and marks the region "complete".  When all the regions
are complete, it begins ignoring HUNGRY messages, and sends the
all done message to stdout.

The commands on stdin are of the form:
func size maxint x y s
where func, size, maxint are ints, and x,y,s are floats.  All must
be present, since its just parsed with a scanf.
function=0 means quit.  
function=1 means compute.

Messages from the master are the DATA messages from the slaves
with the node and function removed.  if it sends 0 bytes for row
-1, then its the all done message.
*/

/* Send image data to the userinterface */
int sendmesg(FILE *out,int *buf)
{
	char cbuff[1024];
	int len,c,p,s;

	for(c=0;c<3;c++)
	{
		cbuff[c*4+0]=(buf[c]&0xFF000000)>>24;
		cbuff[c*4+1]=(buf[c]&0x00FF0000)>>16;
		cbuff[c*4+2]=(buf[c]&0x0000FF00)>>8;
		cbuff[c*4+3]=(buf[c]&0x000000FF);
	}
	fwrite(cbuff,1,3*4,out);

	len=buf[2];
	p=3;
	while(len)
	{
		s=sizeof(cbuff)/4;
		if (len<s)
			s=len;
		for(c=0;c<s;c++)
		{
			cbuff[c*4+0]=(buf[p]&0xFF000000)>>24;
			cbuff[c*4+1]=(buf[p]&0x00FF0000)>>16;
			cbuff[c*4+2]=(buf[p]&0x0000FF00)>>8;
			cbuff[c*4+3]=(buf[p]&0x000000FF);
			p++;
		}
		fwrite(cbuff,1,s*4,out);
		len-=s;
	}
	fflush(stdout);
}

int compute(double x, double y, int maxint)
{
	double tx,ty,t;
	int i=0;

	tx=0.0;
	ty=0.0;
	while((((tx*tx)+(ty*ty))<4.0) && (i<maxint))
	{
		t=tx*tx-ty*ty+x;
		ty=2.0*tx*ty+y;
		tx=t;
		i++;
	}
	return i;
}

void master(void)
{
	int func;
	int siz,maxint;
	float x,y,s;
	int *fin=NULL;
	int intbuf[MAXMESG];
	char *cbuf;
	double **dbuff,skip;
	int i,j;
	int other;
	int done=0;
	MPI_Status stat;

	int me,nproc;

#ifdef DEBUG
	FILE *log;

	log=fopen("/teryx/users/protius/mandelbrot/computelog","w");
#endif

	MPI_Comm_rank(MPI_COMM_WORLD,&me);
	MPI_Comm_size(MPI_COMM_WORLD,&nproc);

	dbuff=malloc(nproc*sizeof(double *));
	for(i=0;i<nproc;i++)
		dbuff[i]=malloc(16*sizeof(double));

	while(1)
	{

#ifdef DEBUG
		fprintf(log,"waiting for command\n");
		fflush(log);
#endif
		scanf("%d %d %d %f %f %f",&func,&siz,&maxint,&x,&y,&s);

#ifdef DEBUG
		fprintf(log,"got command %d %d %d %f %f %f\n",func,siz,maxint,x,y,s);
		fflush(log);
#endif

		if (func==0)  /* userinterface exit, so quit */
		{
			for(i=0;i<nproc;i++)
			{
				dbuff[i][0]=i;
				dbuff[i][1]=DONE;
				MPI_Send(dbuff[i],2,MPI_DOUBLE,i,0,MPI_COMM_WORLD);
			}
		
			MPI_Finalize();
			exit(0);
		}

		skip=s/(double)siz;

		if (fin)
			free(fin);
		fin=malloc(siz*sizeof(int));

		for(i=0;i<siz;i++)
			fin[i]=0;
		done=0;

		for(i=1;i<nproc;i++)
		{
			dbuff[i][0]=i;
			dbuff[i][1]=POKE;
			MPI_Send(dbuff[i],2,MPI_DOUBLE,i,0,MPI_COMM_WORLD);
		}

		while (!done)
		{
			MPI_Recv(intbuf,MAXMESG,MPI_INT,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&stat);
			other=intbuf[0];
	
			switch (intbuf[1])
			{
				case DATA:
					sendmesg(stdout,&intbuf[2]);
#ifdef DEBUG
					fprintf(log,"master: sending data %d %d %d\n",intbuf[0],intbuf[1],intbuf[2]);
#endif
					fin[intbuf[2]]=2;
					break;
				case HUNGRY:
#ifdef DEBUG
					fprintf(log,"master: %d is hungry\n",other);
#endif
					i=0;
					while (i<siz && fin[i])
						i++;
					if (i<siz)
					{
						dbuff[other][0]=other;
						dbuff[other][1]=COMPUTE;
						dbuff[other][2]=x;
						dbuff[other][3]=y+(skip*(double)i);
						dbuff[other][4]=skip;
						dbuff[other][5]=siz;
						dbuff[other][6]=maxint;
						dbuff[other][7]=i;
						dbuff[other][8]=0;
						fin[i]=1;
						MPI_Send(dbuff[other],9,MPI_DOUBLE,other,0,MPI_COMM_WORLD);
					}
			}
			done=1;
			for(i=0;i<siz;i++)
				if (fin[i]!=2)
					done=0;
		}
		intbuf[0]=-1;        /* all done message */
		intbuf[1]=-1;
		intbuf[2]=0;
		sendmesg(stdout,intbuf);

	}
}

void slave(void)
{
	double dbuff[16];
	int intbuff[MAXMESG];
	int me;
	int y;
	double dx,sk;
	int x,c;
	int sz;
	int maxint;
	MPI_Status stat;

	MPI_Comm_rank(MPI_COMM_WORLD,&me);

	while(1)
	{
		MPI_Recv(dbuff,sizeof(dbuff)/sizeof(double),MPI_DOUBLE,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&stat);

		switch((int)(dbuff[1]))
		{
		case DONE:

			MPI_Finalize();
			exit(0);
		break;

		case POKE:
			intbuff[0]=me;
			intbuff[1]=HUNGRY;
			MPI_Send(intbuff,2,MPI_INT,0,1,MPI_COMM_WORLD);
		break;

		case COMPUTE:

			dx=dbuff[2];
			sk=dbuff[4];
			sz=dbuff[5];
			maxint=dbuff[6];
	
			intbuff[0]=me;
			intbuff[1]=DATA;
			intbuff[2]=dbuff[7];
			intbuff[3]=dbuff[8];
			
			c=0;
			for(x=0;x<sz;x++)
				{
				intbuff[5+c]=compute(dx,dbuff[3],maxint);
				dx=dx+sk;
				c++;
				if (c>(MAXMESG/sizeof(int)-8))
				{
					intbuff[4]=c;
					MPI_Send(intbuff,c+5,MPI_INT,0,1,MPI_COMM_WORLD);
					intbuff[3]+=c;
					c=0;
				}
			}
			if (c)
			{
				intbuff[4]=c;
				MPI_Send(intbuff,c+5,MPI_INT,0,1,MPI_COMM_WORLD);
				intbuff[2]+=c;
				c=0;
			}
	
			intbuff[0]=me;
			intbuff[1]=HUNGRY;
			MPI_Send(intbuff,2,MPI_INT,0,1,MPI_COMM_WORLD);
		break;
		}

	}
}
	
int main(int argc, char *argv[])
{
	int me;

	MPI_Init(&argc,&argv);
	MPI_Comm_rank(MPI_COMM_WORLD,&me);

	MPI_Barrier(MPI_COMM_WORLD);

	if (me==0)
		master();
	else
		slave();
}
