#ifndef lint
static char *sccsid = "@(#)mancomp.c	1.1 (UKC) 24/2/88";
#endif  lint
/*
 * Mandelzoom (from Scientific American, August 1985).
 * Recommended places for exploration (according to the article):
 *	 real  imaginary	side
 *	-2.00	 -1.25		2.50	Entire Mandelbrot set
 *	  .26	  0.00		 .01
 *	 -.76	  0.01		 .02
 *	-1.26	  0.01		 .02
 *
 * This program computes the Mandelbrot set, writing the pixels
 * to xxx.pix and the parameters (centre, size, and a pixel density
 * histogram to xxx.his).
 *
 * Option command line (for spawning):
 * 	mandel centre_real centre_imag size npixel niter filename
 * or	mandel xxx.cmd			read commands from a file.
 *
 * Values are:
 *  centre_real	(double) centre of the picture (real part)
 *  centre_imag	(double) centre of the picture (imag part)
 *  size	(double) size of a side (defines the view into the set
 *  npixel	(int)    number of pixels on each side (max == 512)
 *				default = 100
 *  niter	(int)    number of iterations per point.
 *				default = 100
 *  file	(string) name of output file (no type)
 *				default = mandel
 *
 * In "mandel file" format, each line generates a separate picture.
 *
 * Each picture causes two files to be written:
 *   file.his	(readable text format) contains picture definition parameters
 *		and a histogram of pixel values:
 *	centre_real centre_imag side npixel niter \n
 *	bottom top sum	(first non-zero histogram, last non-zero,
 *			  sum of histogram values).
 *	hist[bottom]	(number of pixels with count == bottom)
 *	...		(etc.)
 *	hist[top]	(number of pixels with specified count)
 *  file.pix	(binary format) contains npixel rows, each containing npixel
 *		integers, defining the count at each pixel location.
 *
 * Note: this program is very cpu intensive.  There are a maximum of
 *	4 * niter * (npixel**2) floating-point multiplies
 *     10 * niter * (npixel**2) other floating-point (add, compare, store)
 * per picture.
 *
 * Decus C bug note:
 *   all printf's that format floats must have only one floating-point
 *   parameter, and it must be the last parameter in the argument list.
 *   Also, Decus C doesn't support (double) x++.
 */

#include "mandel.h"

extern char *malloc();
extern char *realloc();
extern char *strcpy();

/*
 * These values may be reset by the user.
 */
int		niter = DEF_NITER;	/* Number of iterations		*/
int		npixel = DEF_NPIXEL;	/* Number of pixels/side	*/
double		centre_real = 0.0;	/* centre of picture            */
double		centre_imag = 0.0;
double		start_real = -2.0;	/* starting coordinates         */
double		start_imag = 2.0;
double		side = 4.0;

int *		hist;			/* Pixel density histogram	*/
int		histsize = 0;		/* number of elements malloced  */
char		line[256];		/* General text work area	*/
char		filename[81];		/* Output file name work area	*/
char		*myargv[MAX_ARGS];	/* To build command arguments	*/
int		myargc;			/* Index into myargv[]		*/
FILE		*pixfd;			/* Pixel file (binary)		*/
FILE		*hisfd;			/* Pixel histogram (text)	*/

extern double	atof();
extern char	*strchr();
char *progname;

main(argc, argv)
int		argc;
char		*argv[];
{
	progname = argv[0];

	if (argc <= 1) {
		fputs("usage: mancomp centre_real centre_imag side npixel niter filename\n", stderr);
		exit(1);
	}

	if (argc == 2 && !isdigit(argv[1][0])) {
		if (freopen(argv[1], "r", stdin) == NULL) {
			perror(argv[1]);
			exit(IO_ERROR);
		}
		while (comfile()) {
			getarguments(myargc, myargv);
			doit();
		}
	} else {
		getarguments(argc, argv);
		doit();
	}
	exit(IO_SUCCESS);
}

doit()
{
	if (setup()) {
		process();
		finish();
	}
}

/*
 * Open the .pix and .his files.  Zero the vectors.
 * set up starting position.
 */
int
setup()
{
	register int		i;
	extern FILE		*fcreate();

	/* allocate space for hist */
	if (niter > histsize) {
		if (histsize == 0) {
			hist = (int *) malloc((unsigned)(sizeof(int) * (niter+1)));
		} else {
			hist = (int *) realloc((char *) hist, (unsigned)(sizeof(int) * (niter+1)));
		}
		if (hist == (int *) NULL) {
			fprintf(stderr, "%s: Out of memory.\n", progname);
			exit(1);
		}
		histsize = niter;
	}

	/*
	 * start is at top left-hand corner of image.
	 */
	start_real = centre_real - side / 2.0;
	start_imag = centre_imag + side / 2.0;

	sprintf(line, "%s.pix", filename);
	if ((pixfd = fopen(line, W_BIN_MODE)) == NULL) {
	    perror(line);
	    sleep(2);
	    return (FALSE);
	}
	sprintf(line, "%s.his", filename);
	if ((hisfd = fcreate(line)) == NULL) {
	    perror(line);
	    sleep(2);
	    fclose(pixfd);
	    return (FALSE);
	}
	fprintf(hisfd, "%f", centre_real);
	fprintf(hisfd, " %f", centre_imag);
	fprintf(hisfd, " %f", side);
	fprintf(hisfd, " %d", npixel);
	fprintf(hisfd, " %d\n", niter);

	/*
	 * flush the file out so we can look at it even if it is unfinished.
	 */
	fflush(hisfd);
	return (TRUE);
}

finish()
/*
 * Compute a histogram of the counts.
 */
{
	register int bottom = 0;		/* first "colour" to print */
	register int top, i;
	register int sum;

	fclose(pixfd);
	for (top = niter - 1; top > bottom  && hist[top] == 0; top--)
	    ;
	for (sum = 0, i = bottom; i <= top; i++)
	    sum = sum + hist[i];
	hist[0] = hist[niter];			/* niter means black */
	fprintf(hisfd, "%d %d %d\n", bottom, top, sum);
	for (i = bottom; i <= top; i++)
	    fprintf(hisfd, "%d\n", hist[i]);
	fclose(hisfd);	   
}	

getarguments(argcount, argstring)
int		argcount;
char		*argstring[];
/*
 * Process argv[] from command line or pseudo argv[] from a file.
 */
{
	register int		i;

	if (argcount < 5) {
	    printf("Missing arguments, need at least\n");
	    printf("centre_real, centre_imaginary, side\n");
	}
	else {
	    centre_real	= atof(argstring[1]);
	    centre_imag	= atof(argstring[2]);
	    side	= atof(argstring[3]);
	    strcpy(filename, DEF_FILENAME);
	    niter	= DEF_NITER;
	    npixel	= DEF_NPIXEL;
	    switch (argcount) {
	    default:
		printf("Extra arguments ignored:\n");
		for (i = 7; i < argcount; i++)
		    printf(" arg[%d] = \"%s\"\n", i, argstring[i]);
	    case 7:
		strcpy(filename, argstring[6]);
	    case 6:
		/* code to do if niter is specified */
		niter = atoi(argstring[5]);
	    case 5:
		npixel = atoi(argstring[4]);
	    }
	    printf("centre = [%f,",	centre_real);
	    printf("%f], ",		centre_imag);
	    printf("side = %f, ",	side);
	    printf("%d pixels, %d iterations\n", npixel, niter);
	}
}

/*
 * Read commands from an indirect command file.
 */
int
comfile()
{
	register char *lp;
	register int i;
	char *gets();

	if (gets(line) == NULL)
	    return (FALSE);
	myargv[0] = "";
	for (myargc = 1, lp = line; *lp != EOS && myargc < MAX_ARGS;) {
	    while (isspace(*lp))
		lp++;
	    myargv[myargc++] = lp;
	    while (*lp != EOS && !isspace(*lp)) {
		lp++;
	    }
	    if (*lp != EOS)
		*lp++ = EOS;
	}
	for (i = 0; i < myargc; i++) {
		printf("arg %d = \"%s\"\n", i, myargv[i]);
	}
	return (TRUE);
}
