#ifndef lint
static char *sccsid = "@(#)mansrv.c	1.1 (UKC) 22/2/88";
#endif  lint

/*
 * Remote server to compute Mandelbrot fractal pattern.
 */

/*
 * Protocol:
 *	All integers are unsigned 16-bit, transmitted LSB first.
 *	All reals are in IEEE 64-bit double precision floating point format.
 * start block:
 *	real	start_real
 *	real	side
 *	int	npixel
 *	int	maxiter
 * continuation block:
 *	real	start_imag
 *
 * reply block:
 *	int	pixel[npixel]
 *
 * The customer issues a connect to the server, and sends the start block.
 * The server replies with the first completed line of data as a reply block.
 * The customer and server then exchange continuation block for reply block
 * until the customer disconnects.
 */

/* dregs from mandel.h */
#define MAX_NPIXEL 512

#include <stdio.h>
#include <sys/tsbsp.h>
#include <sys/tserrno.h>
#include <libtsb.h>
#include "rmandel.h"

extern char *malloc();

#define tsfd 0

unsigned short	pixel[MAX_NPIXEL];	/* Stores one pixel row		*/
double		*gap;			/* Real axis position (mallocked buf) */

static struct start_block sb;
static char tspar2[256], tspar3[256], tspar4[256];
static tscmd_t tsin = {
	0,	/* dummy */
	{
		{ sizeof(sb), (char *) &sb },
		{ sizeof(tspar2), tspar2 },
		{ sizeof(tspar3), tspar3 },
		{ sizeof(tspar4), tspar4 },
	}
};
static tscmd_t tsout = {
	TS_PUSH,
	{
		{ 0/*dummy*/,	  (char *) &pixel[0] },
		{ sizeof(tspar2), tspar2 },
		{ sizeof(tspar3), tspar3 },
		{ sizeof(tspar4), tspar4 },
	}
};

static char errbuf[256];

char *progname;

main(argc, argv)
char **argv;
{
	double start_imag;

	progname = argv[0];

	freopen("/tmp/mansrv.log", "w", stderr);

	/* get header block */
	if (read(tsfd, (char *)&tsin, sizeof(tsin)) != sizeof(tsin)) {
		die("tsread failed to get header block");
	}

	if (tsin.ts_command != TS_PUSH)
		die("start block not PUSHed");
	if (tsin.ts_par[0].ts_nbytes != sizeof(sb)) {
		sprintf(errbuf, "start block wrong size: got %d, expected %d.",
			    tsin.ts_par[0].ts_nbytes, sizeof(sb));
		die("sb wrong size");
	}

	gap = (double *) malloc(sb.npixel * sizeof(*gap));
	if (gap == (double *)0) die("Out of memory");

	/* now we know how much of the pixel buffer we will send */
	tsout.ts_par[0].ts_nbytes = sb.npixel * sizeof(pixel[0]);

	procinit(sb.start_real, sb.side, sb.npixel);

	/* all further data in will just be new start_imags */
	tsin.ts_par[0].ts_data = (char *) &start_imag;
	tsin.ts_par[0].ts_nbytes = sizeof(start_imag);

	while (1) {
		if (read(tsfd, (char *)&tsin, sizeof(tsin)) != sizeof(tsin))
			die("read cont block fails");
		switch (tsin.ts_command) {
		case TS_PUSH:
			if (tsin.ts_par[0].ts_nbytes != sizeof(double))
				die("cont block wrong size");

			process(start_imag,
				(int) sb.npixel,
				(int) sb.maxiter);

			if (write(tsfd, (char *)&tsout, sizeof(tsout))
				!= sizeof(tsout)) die("write fails");
			break;

		case TS_DISCONNECT:
			if (tsin.ts_par[0].ts_data[0] != TE_OK) {
				explaindisc(&tsin);
				die("non-OK disconnect received");
			}
			exit(0);
		default:
			die("Unknown TS command");
		}
	}
}

die(message)
char *message;
{
	fprintf(stderr, "mansrv: %s.\n", message);
	tsdisconnect(tsfd, TE_FAIL, "wherefore", message);
	exit(1);
}

procinit(start_real, side, npixel)
double start_real;		/* position of most negative end of stripe */
double side;			/* distance covered by stripe */
unsigned short npixel;		/* number of pixels to the side */
{
	double float_pixels;
	int j;

	/*
	 * Precompute the position of each pixel on the real axis.
	 * This loop should not be "unrolled" to a succession of
	 * additions as that would lose accuracy.  Given what
	 * follows, the cost isn't excessive.
	 */
	float_pixels = npixel;

	/* Precompute column (real) gap	*/
	for (j = 0; j < npixel; j++)
		gap[j] = start_real + (side * j / float_pixels);
}
	
/*
 * Compute the Mandelbrot set.
 */
process(start_imag, npixel, maxiter)
double start_imag;		/* imaginary value for the stripe */
int npixel;			/* number of pixels to the side */
int maxiter;			/* == maximum value of result */
{
	register int	count;		/* Inner-loop counter		*/
	register int	j;		/* Column counter		*/
	double	z_real, z_imag;		/* Pixel accumulator		*/
	double	z2_real;		/* z_real ** 2			*/
	double	z2_imag;		/* z_imag ** 2			*/
	double	c_real, c_imag;		/* Pixel position (constant)	*/

	c_imag = start_imag;
	for (j = 0; j < npixel; j++) {
		/*
	 	 * Compute one point (pixel) of the Mandelbrot set:
		 * 1. Set z to 0 + 0i and
		 *    set c to the pixel location (real, imag)
		 * 2. Perform step 3 until either
		 *    1. count reaches the selected number of iterations or
		 *    2. the "size" of z exceeds 2.0, where size is
		 *       defined as sqrt(z_real**2 + z_imag**2)
		 *       (we don't bother with the square root.)
		 * 3. z = z**2 + c;
		 *    count = count + 1;
		 *    size = size of z as defined above.
		 * 4. Set pixel[i,j] to the number of iterations (count).
		 */
		c_real = gap[j];
		z_real = c_real;
		z_imag = c_imag;
		for (count = 0; count < maxiter; count++) {
		    if (((z2_real = (z_real * z_real))
		       + (z2_imag = (z_imag * z_imag))) > 4.0)
			break;
		    z_imag = (z_real * z_imag * 2.0) + c_imag;
		    z_real = z2_real - z2_imag + c_real;
		}
		pixel[j] = count;		/* Store the pixel	*/
	}					/* All columns this row	*/
}

/*
 *	Turn disconnect packet into text on stderr for user
 */
explaindisc(response)
tscmd_t *response;
{
	fprintf(stderr, "%s: %s", progname,
		tserror(response->ts_par[0].ts_data[0]) );

	/* Location */
	if (response->ts_par[1].ts_nbytes > 0) {
		fprintf(stderr, ", location=\"%*s\"", 
			response->ts_par[1].ts_nbytes,
			response->ts_par[1].ts_data);
	}

	/* Explanatory text */
	if (response->ts_par[2].ts_nbytes > 0) {
		fprintf(stderr, ", explan=\"%*s\"", 
			response->ts_par[2].ts_nbytes,
			response->ts_par[2].ts_data);
	}
	fputs(".\n", stderr);
}
