 /*  Xsand, a sandpile simulator for X windows.

    Michael Creutz   
    creutz@wind.phy.bnl.gov 

    compiling:  cc -O -o xsand -L/usr/X11R6/lib xsand.c -lX11 

    version 1.5 
    24 November 1995 
    LZW disabled, 10 October 1998

    minor modifications to eliminate gcc -Wall warnings, March 1999

    The latest version is kept at "http://thy.phy.bnl.gov/www/xtoys/xtoys.html"
    A text description of this program is there as well.
  */

/* because of the unisys patent on LZW, it may be illegal to
   distribute this program with the following "if" modified to "while"*/
#define LZW if

# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/Xos.h>
# include <X11/Xatom.h>
# include <X11/cursorfont.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <malloc.h>

# define BPW (sizeof(long))

/* size and position parameters for buttons, etc. */
# define PLAYTOP 116
# define PLAYLEFT 10
# define BOUNDHEIGHT 72
# define TRACEHEIGHT 36
# define BOUNDWIDTH  112
# define BARHEIGHT 15
# define BARWIDTH  136
# define BARTOP 8
# define BARLEFT 40
# define SRHEIGHT (3*18)
# define BUTTONWIDTH 68

int	nrows,
	ncols,
	volume;	/* lattice dimensions (plus two for boundaries) */
     /* ncols will be truncated to 
        a multiple of bytes per long word (BPW),
        and then two units are lost for boundaries    */
int	block,		/* size of cells displayed on the screen          */
	blockshift;	/* log_2(block)                                   */
unsigned char	*field[2] = { NULL, NULL };	/* pointers to old and new system */
int	old = 0, new = 1;		/* which field is current? */

int	paused = 0,		/* is the system paused?                  */
	boundary,		/* 0,1,2,3 for open, live, periodic, flow */
	pencolor = 4,		/* for sketching                          */
	trace,			/* is the tracer on?                      */
	colshift,		/* how to shift to get the next column    */
	active = 1,
	autodouble = 0;
long	mask0x13, 
	mask0x01, 
	mask0x03, 
	mask0x10;		/* some useful masks */
char	stringbuffer[256];	/* generally useful 
				   things for regulating the updating speed */
int	speed = 0;		/* updating delay proportional to speed   */
int	delay = 0;		/* counter for implementing speed control */
struct timeval	timeout;	/* timer for speed control */

/* various window stuff */
Display		*display;
int		screen;
static char	*progname;
Window		window, 
		quitbutton, 
		pausebutton, 
		playground, 
		srbutton,
		blockbutton,
		boundwindow, 
		colorbar, 
		fillbutton, 
		tracewindow, 
		doublebutton,
		adbutton, 
		speedbutton, 
		makebutton ();
XColor		xcolor,
		colorcell;
Colormap	cmap;
GC		gc, 
		gcpen;
int		windowwidth,
		windowheight;
XFontStruct	*font = NULL;
int		font_height, 
		font_width;
XSizeHints	size_hints;
int		darkcolor,
		lightcolor,
		black,
		white;
XImage		*spinimage = NULL;
long		translate[256];		/* for converting colors   */
char		*boundtext[4] = { "open", "sandy", "periodic", "flow" };
char		*srtext[3] = { "save", "restore", "+ saved" };
void		drawbutton (),
		settrace (),
		openwindow (),
		makebuttons (),
		update (),
		repaint (),
		cleanup (),
		fixboundary (),
		showpic (),
		setboundary (),
		sketch (),
		savepic (),
		addpic (),
		setpen (),
		parallelupdate (),
		decompress (),
		compress (), 
		inittable ();


	
void	main (argc, argv)
	int	argc;
	char	**argv;
{
	unsigned int 	width, height;
	int		i, x, y;
	long 		bitcheck;
	XEvent 		report;
	
	
	progname = argv[0];

	/* initial lattice size */
	block = 1;
	nrows = 200 / block;
	ncols = (~(BPW - 1)) & (200 / block);
	volume = nrows * ncols;
	trace = 0;
	boundary = 0;
	/* open the window and make the buttons */
	openwindow (argc, argv);
	makebuttons ();

	/* set the masks for parallel updating */
	for (i = 0; i < sizeof (long); i++){
		mask0x13 = 0x13 | (mask0x13 << 8);
		mask0x01 = 0x01 | (mask0x01 << 8);
		mask0x03 = 0x03 | (mask0x03 << 8);
		mask0x10 = 0x10 | (mask0x10 << 8);
	}
	
	/* check for byte order in a word */
	bitcheck = 1;
	if (*(char *) (&bitcheck))
		colshift = (-1);	/* LSBfirst */
	else
		colshift = 1;		/* MSBfirst */

	/* loop forever, looking for events */
	while (1){
		if (0 == paused){
			if (delay){/* don't update yet */
				delay--;
				/* this use of select() seems a 
				 * kludge to me; 
				 * why can't usleep() be 
				 * more standard? */
				timeout.tv_sec = 0;
				timeout.tv_usec = 100000;
				/* .1 sec per delay unit */
				
				select (0, NULL, NULL, NULL, &timeout);
			}else{
				delay = speed;
				update ();
			}
		}
		
		if (	paused | 
			XPending (display) |
			((active | autodouble) == 0)	){
			XNextEvent (display, &report);
			
			switch (report.type){
				case Expose:
					if ((report.xexpose.window) != window)
						break;	/* cuts down 
							   flashing,
							   but you 
							   might remove 
							   this line if 
							   things aren't 
							   being redrawn */
					if (report.xexpose.count != 0)
						break;	/* more in queue, 
							   wait for them */
					
					repaint ();
					break;
				case ConfigureNotify:
					width = report.xconfigure.width;
					height = report.xconfigure.height;
					if (	(width < size_hints.min_width)
						||(height < size_hints.min_height)){
						fprintf(	stderr,
								"%s: window too small to proceed.\n",
								progname);
						cleanup ();
					}else if((width != windowwidth)
						|| (height != windowheight)){
						windowwidth = width;
						windowheight = height;
						XSetForeground (display,
								gcpen,
								lightcolor);
						XFillRectangle (display,
								window, gcpen,
								0, 0,
								windowwidth,
								windowheight);
						nrows =
							(height - PLAYTOP -
							 10) / block;
						ncols =
							(~(BPW - 1)) &
							((width
							  - PLAYLEFT -
							  BOUNDWIDTH -
							  20) / block);
						volume = nrows * ncols;
						makebuttons ();
						fixboundary ();
						showpic ();
					}
				      break;
				case ButtonPress:
				      if (report.xbutton.window == quitbutton)
					      cleanup ();
				      else if (report.xbutton.window ==
					       pausebutton){
					      
						paused = 1 - paused;
						drawbutton (pausebutton, 0, 0,
							    BUTTONWIDTH, 18,
							    "pause",
							    1 - 2 * paused);
					}else if (report.xbutton.window ==
					       fillbutton){
						drawbutton (fillbutton, 0, 0,
							    BUTTONWIDTH, 18,
							    "fill", -1);
						for (i = 0; i < volume; i++)
							field[old][i] =
								pencolor;
						fixboundary ();
						showpic ();
						drawbutton (fillbutton, 0, 0,
							    BUTTONWIDTH, 18,
							    "fill", 1);
					}else if (report.xbutton.window ==
					       boundwindow){
						setboundary (
							     (report.xbutton.
							      y * 4) /
							     BOUNDHEIGHT);
						showpic ();
					}else if (report.xbutton.window ==
					       tracewindow)
					      settrace ((report.xbutton.y * 2)
							/ TRACEHEIGHT);
				      else if (report.xbutton.window == playground)	/* sketch */{
						x = report.xbutton.x / block;
						y = report.xbutton.y / block;
						sketch (x, y);
					}else if (report.xbutton.window == srbutton)	/* save or restore */{
						y =
						(report.xbutton.y *3) / SRHEIGHT;
						drawbutton (srbutton, 0,
							    y * (SRHEIGHT /
								 3),
							    BUTTONWIDTH,
							    SRHEIGHT / 3,
							    srtext[y], -1);
						if (0 == y)
							savepic (field[old],
								 ncols,
								 nrows);
						else if (1 == y)	/* load in gif image */{
							for (i = 0;
							       i < volume;
							       i++)
								  field[old]
									  [i]
									  = 0;
							  addpic (field[old],
								  ncols,
								  nrows);
							  showpic ();
						  }else	/* add gif image mod 8 to existing field */{
							  addpic (field[old],
								  ncols,
								  nrows);
							  for (i = 0;
							       i < volume;
							       i++)
								  field[old]
									  [i]
									  &=
									  7;
							  showpic ();
						  }
						drawbutton (srbutton, 0,
							    y * (SRHEIGHT /
								 3),
							    BUTTONWIDTH,
							    SRHEIGHT / 3,
							    srtext[y], 1);
					}else if (report.xbutton.window ==
					       blockbutton){
						i =
				(1 << (4 * report.xbutton.x /BOUNDWIDTH));
						if (i != block)	/* reset for new block size */{
							  block = i;
							  nrows =
								  (windowheight
								   - PLAYTOP -
								   10) /
								  block;
							  ncols =
								  (~(BPW - 1))
								  &
								  (
								   (windowwidth
								    -
								    PLAYLEFT -
								    BOUNDWIDTH
								    -
								    20) /
								   block);
							  volume =
								  nrows *
								  ncols;
							  XSetForeground
								  (display,
								   gcpen,
								   lightcolor);
							  XFillRectangle
								  (display,
								   window,
								   gcpen, 0,
								   0,
								   windowwidth,
								   windowheight);
							  makebuttons ();
							  fixboundary ();
							  showpic ();
						  }
					}else if (report.xbutton.window ==
					       colorbar){
						setpen ((report.xbutton.x * 8)
							/ BARWIDTH);
					}else if (report.xbutton.window ==
					       adbutton){
						autodouble = 1 - autodouble;
						drawbutton (adbutton, 0, 0,
							    BUTTONWIDTH, 18,
							    "auto-d",
							    1 -
							    2 * autodouble);
					}else if (report.xbutton.window ==
					       doublebutton){
						drawbutton (doublebutton, 0,
							    0, BUTTONWIDTH,
							    18, "double", -1);
						for (i = 0; i < volume; i++)
							field[old][i] =
								7 &
								(field[old][i]
								 << 1);
						active = 1;
						fixboundary ();
						showpic ();
						drawbutton (doublebutton, 0,
							    0, BUTTONWIDTH,
							    18, "double", 1);
					}else if (report.xbutton.window ==
					       speedbutton){
						/* reset speed */
						speed =
							10 -
							(11 *
							 report.xbutton.x) /
							BOUNDWIDTH;
						if (speed < 0)
							speed = 0;
						if (speed > 10)
							speed = 10;
						delay = speed;
						drawbutton (speedbutton, 0, 0,
							    BOUNDWIDTH, 18,
							    "speed", -1);
						drawbutton (speedbutton,
							    1 +
							    ((10 - speed) *
							     (BOUNDWIDTH -
							      2)) / 11, 1,
							    (BOUNDWIDTH -
							     2) / 11, 16, "",
							    2);
					}else
					      update ();
					/* do a sweep when mouse clicked */
					break;
				default:
					break;
			      }	/* end of switch */
		    }		/* end of if XPending */
	  }			/* end of while(1) */
}				/* end of main */


	
void	sketch (x, y)	/* sketch in playground starting at point x,y */
	int 	x, y;
{
	int		sketching, 
			i, 
			oldx, 
			oldy, 
			deltax, 
			deltay, 
			newx, 
			newy;
	Window		root,
			child;
	XEvent		sketchreport;
	unsigned int	keys_buttons;
	int		window_x, 
			window_y;
	
	
	newx = x;
	newy = y;
	field[old][x + ncols * y] = pencolor;
	sketching = 1;
	while (sketching){
		showpic ();
		XNextEvent (display, &sketchreport);
		switch (sketchreport.type){
			case MotionNotify:
				oldx = newx;
				oldy = newy;
				if (!XQueryPointer(	display, 
							playground,
							&root,
							&child,
							&window_x,
							&window_y, 
							&newx,
							&newy,
							&keys_buttons)){
					sketching = 0;
					break;
				}
				
				if (blockshift){
				      newx >>= blockshift;
				      newy >>= blockshift;
				}
				if (	(newx > ncols) | 
					(newy > nrows) | 
					(newx < 0) |
					(newy < 0)){
				      sketching = 0;
				      break;
				}
				
				deltax = abs (newx - oldx);
				deltay = abs (newy - oldy);
				if (deltax > deltay)
				    for (i = 1; i <= deltax; i++)
					    field[old][oldx +
						       i * (newx -
							    oldx) / deltax +
						       ncols * (oldy +
								i * (newy -
								     oldy) /
								deltax)] =
						    pencolor;
				else
				    for (i = 1; i <= deltay; i++)
					    field[old][oldx +
						       i * (newx -
							    oldx) / deltay +
						       ncols * (oldy +
								i * (newy -
								     oldy) /
								deltay)] =
						    pencolor;
				break;
			case ButtonRelease:
			default:
				sketching = 0;
				break;
		}
	}
	fixboundary ();
	showpic ();
	return;
}

void	update ()
{
	int	i;
	
	
	parallelupdate(field[old], field[new]);
	old = new;
	new = 1 - new;
	fixboundary ();
	showpic ();
	if ((0 == active) && autodouble){
		  for (i = 0; i < volume; i++)
			  field[old][i] = 7 & (field[old][i] << 1);
		  fixboundary ();
		  showpic ();
	}
	return;
}

void	parallelupdate (source, destination)
/* do several spins at a time */
	long 	*source;
	long 	*destination;
{
	int	i,
		maxi = volume / BPW;
	
	active = 0;
	
	for (i = 0; i < maxi; i++){
		  destination[i] = source[i] & mask0x13;
		  active |= (source[i] = (source[i] >> 2) & mask0x01);
	  }
	if (trace)
		for (i = 0; i < maxi; i++)
			destination[i] |= source[i] << 4;
	
	maxi = (volume - ncols) / BPW;
	for (i = ncols / BPW; i < maxi; i++)
		destination[i] += (source[i] >> 8)
			+ (source[i - colshift] << (8 * (BPW - 1)))
			+ (source[i] << 8)
			+ (source[i + colshift] >> (8 * (BPW - 1)))
			+ source[i + ncols / BPW] + source[i - ncols / BPW];
	
	
	return;
}

void	showpic ()		/* display the field */
{
	int	row,
		col,
		i1,
		i2,
		color,
		j,
		j1, 
		j2, 
		blocktop = block;
	char 	*picture = (*spinimage).data;
	
	
	if (block > 4)
		blocktop = block - 1;
	if (8 == (*spinimage).depth){
		  if (block > 1)/* I wish I knew how to do this faster */
			  for (row = 0; row < nrows; row++)
				  for (col = 0; col < ncols; col++){
					    color =
						    translate[field[old]
							      [row * ncols +
							       col]];
					    j =
						    block * (col +
							     block * ncols *
							     row);
					    if (color != picture[j])
						    for (i1 = 0;
							 i1 < blocktop; i1++){
							      j1 =
								      i1 *
								      block *
								      ncols +
								      j;
							      for (i2 = 0;
								   i2 <
								   blocktop;
								   i2++)
								      picture
									      [j1
									       +
									       i2]
									      =
									      color;
						    }
				  }else{
					  for (j = 0; j < volume; j++)
						  picture[j] = translate[field[old][j]];
				  }
	}else{
	/* depth is not 8, use xputpixel (this is really ugly) */
		if (block > 1)	/* I wish I knew how to do this faster */
			  for (row = 0; row < nrows; row++)
				  for (col = 0; col < ncols; col++){
					    color =
						    translate[field[old]
							      [row * ncols +
							       col]];
					    if (color !=
						XGetPixel (spinimage, j1 =
							   block * col, j2 =
							   block * row))
						    for (i2 = 0;
							 i2 < blocktop; i2++)
							    for (i1 = 0;
								 i1 <
								 blocktop;
								 i1++)
								    XPutPixel
									    (spinimage,
									     j1
									     +
									     i1,
									     j2
									     +
									     i2,
									     color);
				  }else
					  for (row = 0; row < nrows; row++){
						  j1 = row * ncols;
						  for (col = 0; col < ncols; col++)
					    XPutPixel (spinimage, col, row,
						       translate[field[old]
								 [j1 + col]]);
					  }
	}
	
	XPutImage (	display, 
			playground, 
			gc, 
			spinimage, 
			0, 
			0, 
			0, 
			0,
			block * ncols, 
			block * nrows);
	return;
}

void
fixboundary ()
{
	int	i,
		bv;
		
	if (boundary < 2){
		/* dead or alive */
		bv = 4 * boundary;
		for (i = 0; i < ncols; i++){
			field[old][i] = bv;
			field[old][volume - 1 - i] = bv;
		}
		
		for (i = 0; i < volume; i += ncols){
			field[old][i] = bv;
			field[old][i + ncols - 1] = bv;
		}
	  }else if (2 == boundary){
		  /* periodic */
		  for (i = 0; i < ncols; i++){
			    field[old][i] =
				    field[old][volume - 2 * ncols + i];
			    field[old][volume - ncols + i] =
				    field[old][ncols + i];
		  }
		  
		  for (i = 0; i < volume; i += ncols){
			    field[old][i] = field[old][i + ncols - 2];
			    field[old][i + ncols - 1] = field[old][i + 1];
		  }
	  }else{
		  /* flow: alive on top, dead on sides and bottom */
		  for (i = 0; i < ncols; i++){
			    field[old][i] = 4;
			    field[old][volume - 1 - i] = 0;
		  }
		  for (i = 0; i < volume; i += ncols){
			    field[old][i] = 0;
			    field[old][i + ncols - 1] = 0;
		  }
	  }
	
	return;
}

void	repaint ()
/* this fixes the window up whenever it is uncovered */
{
	int	i;
	
	drawbutton (pausebutton, 0, 0, BUTTONWIDTH, 18, "pause",
		    1 - 2 * paused);
	drawbutton (quitbutton, 0, 0, BUTTONWIDTH, 18, "quit", 1);
	drawbutton (fillbutton, 0, 0, BUTTONWIDTH, 18, "fill", 1);
	drawbutton (doublebutton, 0, 0, BUTTONWIDTH, 18, "double", 1);
	drawbutton (adbutton, 0, 0, BUTTONWIDTH, 18, "auto-d",
		    1 - 2 * autodouble);
	drawbutton (window, PLAYLEFT - 4, PLAYTOP - 4, block * ncols + 8,
		    block * nrows + 8, NULL, 4);
	drawbutton (speedbutton, 0, 0, BOUNDWIDTH, 18, "speed", -1);
	drawbutton (speedbutton, 1 + ((10 - speed) * (BOUNDWIDTH - 2)) / 11,
		    1, (BOUNDWIDTH - 2) / 11, 16, "", 2);
	
	for (i = 0; i < 3; i++)
		drawbutton (srbutton, 0, i * (SRHEIGHT / 3), BUTTONWIDTH,
			    SRHEIGHT / 3, srtext[i], 1);

	/* fix the block buttons */
	for (i = 1; i <= 4; i++){
		sprintf (stringbuffer, "%d", (1 << i) / 2);
		drawbutton (blockbutton, (i - 1) * BOUNDWIDTH / 4, 0,
			      BOUNDWIDTH / 4, 18, stringbuffer,
			      1 - 2 * (i == (blockshift + 1)));
	}
	
	XDrawString (display, window, gc, BARLEFT + 24, BARTOP + 68,
		     "cell size", 9);

	/* draw the boundary condition buttons */
	for (i = 0; i < 4; i++)
		drawbutton (boundwindow, 0, i * BOUNDHEIGHT / 4, BOUNDWIDTH,
			    BOUNDHEIGHT / 4, boundtext[i],
			    1 - 2 * (i == boundary));
	
	XDrawString (display, window, gc, windowwidth - BOUNDWIDTH,
		     BOUNDHEIGHT + font_height + 2, "  Boundary  ", 12);

	/* draw the trace buttons */
	drawbutton (tracewindow, 0, 0, BOUNDWIDTH, TRACEHEIGHT / 2,
		    "tracer", 1 - 2 * trace);
	drawbutton (tracewindow, 0, TRACEHEIGHT / 2, BOUNDWIDTH,
		    TRACEHEIGHT / 2, "clear trace", 1);

	/* fix the colorbar */
	for (i = 0; i < 8; i++){
		  drawbutton (colorbar, i * BARWIDTH / 8, 0,
			      BARWIDTH / 8, BARHEIGHT, NULL,
			      2 - 4 * (i == pencolor));
		  XSetForeground (display, gcpen, translate[i]);
		  XFillRectangle (display, colorbar, gcpen,
				  i * BARWIDTH / 8 + 2, 2, BARWIDTH / 8 - 4,
				  BARHEIGHT - 4);
		  sprintf (stringbuffer, "%d", i);
		  XDrawString (display, window, gc,
			       4 + BARLEFT + BARWIDTH * i / 8, BARTOP + 28,
			       stringbuffer, 1);
	}

	/* write various strings */
	sprintf (stringbuffer, "%d by %d lattice  ", ncols - 2, nrows - 2);
	XDrawImageString (display, window, gc, BARLEFT - 14, 100,
			  stringbuffer, strlen (stringbuffer));
	XDrawString (display, window, gc, windowwidth - 44, windowheight - 5,
		     "MJC", 3);
	showpic ();
	return;
}

void	setboundary (value)
/* take action if boundary buttons hit */
     int	value;
{
	drawbutton (boundwindow, 0,
		    (BOUNDHEIGHT * boundary) / 4, BOUNDWIDTH, BOUNDHEIGHT / 4,
		    boundtext[boundary], 1);
	boundary = value;
	fixboundary ();
	drawbutton (boundwindow, 0,
		    (BOUNDHEIGHT * boundary) / 4, BOUNDWIDTH, BOUNDHEIGHT / 4,
		    boundtext[boundary], -1);
	return;
}

void	settrace (value)
/* take action if trace buttons hit */
     int	value;
{
	int	i;
	if (1 == value){
		/* clear the trace bit */
		  drawbutton (tracewindow, 0, TRACEHEIGHT / 2, BOUNDWIDTH,
			      TRACEHEIGHT / 2, "clear trace", -1);
		  for (i = 0; i < volume; i++)
			  field[old][i] &= 0xf;
		  showpic ();
		  drawbutton (tracewindow, 0, TRACEHEIGHT / 2, BOUNDWIDTH,
			      TRACEHEIGHT / 2, "clear trace", 1);
	}else{
		  trace = 1 - trace;
		  drawbutton (tracewindow, 0, 0, BOUNDWIDTH, TRACEHEIGHT / 2,
			      "tracer", 1 - 2 * trace);
	}
	return;
}


void setpen (value)
/* take action if pen buttons hit */
     int	value;
{
	drawbutton (colorbar, pencolor * BARWIDTH / 8, 0,
		    BARWIDTH / 8, BARHEIGHT, NULL, 2);
	XSetForeground (display, gcpen, translate[pencolor]);
	XFillRectangle (display, colorbar, gcpen, pencolor * BARWIDTH / 8 + 2,
			2, BARWIDTH / 8 - 4, BARHEIGHT - 4);
	pencolor = value;
	drawbutton (colorbar, pencolor * BARWIDTH / 8, 0,
		    BARWIDTH / 8, BARHEIGHT, NULL, -2);
	XSetForeground (display, gcpen, translate[pencolor]);
	XFillRectangle (display, colorbar, gcpen, pencolor * BARWIDTH / 8 + 2,
			2, BARWIDTH / 8 - 4, BARHEIGHT - 4);
	return;
}

void	openwindow (argc, argv)
/* a lot of this is taken from basicwin in the Xlib Programming Manual */
     int	argc;
     char	**argv;
{
	char			*window_name = "Sand";
	char			*icon_name = "Sand";
	long			event_mask;
	Pixmap			icon_pixmap;
	char			*display_name = NULL;
	int			i;
# define icon_bitmap_width 16
# define icon_bitmap_height 16
	static unsigned char	icon_bitmap_bits[] = {
		0x1f, 0xf8, 0x1f, 0x88, 0x1f, 0x88, 0x1f, 0x88, 0x1f, 0x88,
			0x1f, 0xf8,
		0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8,
			0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
	};

/* open up the display */
	if ((display = XOpenDisplay (display_name)) == NULL){
		  fprintf (stderr, "%s: cannot connect to X server %s\n",
			   progname, XDisplayName (display_name));
		  exit (-1);
	}
	
	screen = DefaultScreen (display);
	cmap = DefaultColormap (display, screen);

	darkcolor = black = BlackPixel (display, screen);
	lightcolor = white = WhitePixel (display, screen);
	translate[0] = white;
	translate[1] = black;
	translate[2] = lightcolor;
	translate[3] = darkcolor;
	if (XAllocNamedColor (display, cmap, "salmon", &colorcell, &xcolor))
		darkcolor = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "wheat", &colorcell, &xcolor))
		lightcolor = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "grey", &colorcell, &xcolor))
		translate[0] = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "red", &colorcell, &xcolor))
		translate[1] = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "blue", &colorcell, &xcolor))
		translate[2] = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "green", &colorcell, &xcolor))
		translate[3] = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "orange", &colorcell, &xcolor))
		translate[4] = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "gold", &colorcell, &xcolor))
		translate[5] = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "yellow", &colorcell, &xcolor))
		translate[6] = colorcell.pixel;
	if (XAllocNamedColor (display, cmap, "white", &colorcell, &xcolor))
		translate[7] = colorcell.pixel;
	if (XAllocNamedColor
	    (display, cmap, "turquoise", &colorcell, &xcolor))
		translate[8] = colorcell.pixel;

	/* fill out the color table for future uses */
	for (i = 9; i < 256; i++)
		translate[i] = translate[i % 8];
	for (i = 16; i < 20; i++)
		translate[i] = translate[8];	/* for tracing */

	/* make the main window */
	windowwidth = (block * ncols + PLAYLEFT + BOUNDWIDTH + 20);
	windowheight = (PLAYTOP + block * nrows + 10);
	window = XCreateSimpleWindow (display, RootWindow (display, screen),
				      0, 0, windowwidth, windowheight, 4,
				      black, lightcolor);

/* make the icon */
	icon_pixmap = XCreateBitmapFromData (display, window,
					     icon_bitmap_bits,
					     icon_bitmap_width,
					     icon_bitmap_height);

	size_hints.flags = PPosition | PSize | PMinSize;
	size_hints.min_width = windowwidth;
	size_hints.min_height = windowheight;
#ifdef X11R3
	size_hints.x = x;
	size_hints.y = y;
	size_hints.width = windowwidth;
	size_hints.height = windowheight;
	XSetStandardProperties (display, window, window_name, icon_name,
				icon_pixmap, argv, argc, &size_hints);
#else
	{
		XWMHints wm_hints;
		XClassHint class_hints;
		XTextProperty windowName, iconName;
		if (XStringListToTextProperty (&window_name, 1, &windowName)
		    == 0){
			fprintf (	stderr,
					"%s: structure allocation for windowName failed.\n",
					progname);
			exit (-1);
		}
		if (XStringListToTextProperty (&icon_name, 1, &iconName) == 0){
			fprintf (	stderr,
					"%s: structure allocation for iconName failed.\n",
					progname);
			exit (-1);
		  }
		wm_hints.initial_state = NormalState;
		wm_hints.input = True;
		wm_hints.icon_pixmap = icon_pixmap;
		wm_hints.flags = StateHint | IconPixmapHint | InputHint;
		class_hints.res_name = progname;
		class_hints.res_class = "Basicwin";
		XSetWMProperties (display, window, &windowName, &iconName,
				  argv, argc, &size_hints, &wm_hints,
				  &class_hints);
	}
#endif

/* pick the events to look for */
	event_mask = ExposureMask | ButtonPressMask | StructureNotifyMask;
	XSelectInput (display, window, event_mask);

/* pick font: 9x15 is supposed to almost always be there,
     the fixed font option put in by Peter Chang */
	if ((font = XLoadQueryFont (display, "9x15")) == NULL)
		if ((font = XLoadQueryFont (display, "fixed")) == NULL){
			  fprintf (stderr,
				   "%s: Sorry, having font problems.\n",
				   progname);
			  exit (-1);
		}
	font_height = font->ascent + font->descent;
	font_width = font->max_bounds.width;

/* make graphics contexts: 
      gc for black on white
      gcpen for varying purposes */

	gc = XCreateGC (display, window, 0, NULL);
	XSetFont (display, gc, font->fid);
	XSetForeground (display, gc, black);
	XSetBackground (display, gc, lightcolor);

	gcpen = XCreateGC (display, window, 0, NULL);
	XSetFont (display, gcpen, font->fid);
	XSetForeground (display, gcpen, translate[pencolor]);
	XSetBackground (display, gcpen, lightcolor);

/* show the window */
	XMapWindow (display, window);
	return;
}

void	makebuttons ()
{
	int	i,
		j;
	long 	event_mask;
	XEvent	report;
	Cursor	cursor;
	
	/* first destroy any old buttons */
	XDestroySubwindows (display, window);
	
	/* now make the new buttons */
	quitbutton =
		makebutton (windowwidth - 92, windowheight - 38, BUTTONWIDTH,
			    18);
	pausebutton =
		makebutton (windowwidth - 92, windowheight - 38 - 18,
			    BUTTONWIDTH, 18);
	fillbutton =
		makebutton (windowwidth - 92, windowheight - 38 - 2 * 18,
			    BUTTONWIDTH, 18);
	doublebutton =
		makebutton (windowwidth - 92, windowheight - 38 - 3 * 18,
			    BUTTONWIDTH, 18);
	adbutton =
		makebutton (windowwidth - 92, windowheight - 38 - 4 * 18,
			    BUTTONWIDTH, 18);
	srbutton =
		makebutton (windowwidth - 92, windowheight - 38 - 7 * 18,
			    BUTTONWIDTH, 18 * 3);
	boundwindow =
		makebutton (windowwidth - BOUNDWIDTH - 4, 4, BOUNDWIDTH,
			    BOUNDHEIGHT);
	tracewindow =
		makebutton (windowwidth - BOUNDWIDTH - 4, BOUNDHEIGHT + 25,
			    BOUNDWIDTH, TRACEHEIGHT);
	colorbar = makebutton (BARLEFT, BARTOP, BARWIDTH, BARHEIGHT);
	blockbutton = makebutton (BARLEFT + 12, BARTOP + 36, BOUNDWIDTH, 18);
	speedbutton = makebutton (windowwidth - BOUNDWIDTH - 4,
				  BOUNDHEIGHT + TRACEHEIGHT + 30, BOUNDWIDTH,
				  18);

	playground = XCreateSimpleWindow (display, window,
					  PLAYLEFT, PLAYTOP, block * ncols,
					  block * nrows, 0, black,
					  translate[8]);
	event_mask =
		ExposureMask | ButtonReleaseMask | ButtonPressMask |
		PointerMotionHintMask | ButtonMotionMask;
	XSelectInput (display, playground, event_mask);
	XMapWindow (display, playground);

	/* wait for playground to be displayed befor proceeding */
	i = 1;			/* a flag */
	while (i){
		XNextEvent (display, &report);
		switch (report.type){
			case Expose:
			    if (report.xexpose.window != playground)
				    i = 0;
			default:
			    break;
		}
	}
	/* make image structure */
	if (NULL != spinimage){
		  XDestroyImage (spinimage);
		  spinimage = NULL;
	}
	
	spinimage = XGetImage ((Display *) display, (Drawable) playground,
			       0, 0, block * ncols, block * nrows, AllPlanes,
			       ZPixmap);
	
	if (NULL == spinimage){
		  fprintf (stderr, "trouble creating image structure\n");
		  exit (-1);
	}
	
	/* make special cursors to be cute */
	cursor = XCreateFontCursor (display, XC_sb_up_arrow);
	XDefineCursor (display, playground, cursor);
	cursor = XCreateFontCursor (display, XC_hand2);
	XDefineCursor (display, doublebutton, cursor);
	XDefineCursor (display, colorbar, cursor);
	XDefineCursor (display, boundwindow, cursor);
	XDefineCursor (display, tracewindow, cursor);
	XDefineCursor (display, srbutton, cursor);
	XDefineCursor (display, blockbutton, cursor);
	XDefineCursor (display, fillbutton, cursor);
	XDefineCursor (display, adbutton, cursor);
	XDefineCursor (display, fillbutton, cursor);
	XDefineCursor (display, quitbutton, cursor);
	XDefineCursor (display, pausebutton, cursor);
	XDefineCursor (display, speedbutton, cursor);

	/* reallocate various arrays */
	/* allocate a few bytes of extra space at ends of field array */
	for (i = 0; i < 2; i++)
	  {
		  if (NULL != field[i])
			  free ((char *) field[i] - BPW);
		  if (NULL ==
		      (field[i] =
		       (unsigned char *) malloc (volume + 2 * BPW)))
		    {
			    fprintf (stderr, "allocation problems\n");
			    cleanup ();
		    }
		  /* clear initial state */
		  for (j = 0; j < (volume + 2 * BPW); j++)
			  field[i][j] = pencolor;
		  field[i] += BPW;
	  }
	/* set blockshift to log_2 of block */
	blockshift = 0;
	i = block;
	while (i >>= 1)
		blockshift++;
	/* draw everything */
	repaint ();
	return;
}

Window makebutton (xoffset, yoffset, xsize, ysize)
     int	xoffset, 
     		yoffset, 
		xsize, 
		ysize;
/* Puts button of specified dimensions on window.  Nothing is drawn. */
{
	Window	buttonwindow;
	long	event_mask;

	
	buttonwindow = XCreateSimpleWindow (display, window, xoffset, yoffset,
					    xsize, ysize, 0, black,
					    lightcolor);
	event_mask = ButtonPressMask | ExposureMask;	/* look for mouse-button presses */
	XSelectInput (display, buttonwindow, event_mask);
	XMapWindow (display, buttonwindow);
	
	return buttonwindow;
}

void	drawbutton (buttonwindow, xoffset, yoffset, xsize, ysize, text, state)
     Window	buttonwindow;
     int	xoffset,
     		yoffset,
		xsize,
		ysize,
		state;
     char	*text;
/* Draw a button in buttonwindow of specified dimensions with text
   centered.  Color of button determined by sign of "state." 
   size of border determined by magnitude. */
{
	int	textlength, 
		i, 
		j;
	int	cdark,
		clight,
		cup,
		cdown;
	int	cleft,
		cright,
		cbutton,
		ctext;

		
	cup = lightcolor;
	cdown = darkcolor;
	cdark = black;
	clight = white;
	if (state < 0){
		  cbutton = cdown;
		  ctext = clight;
		  cleft = cdark;
		  cright = clight;
	}else{
		  cbutton = cup;
		  ctext = cdark;
		  cleft = clight;
		  cright = cdark;
	}
	
	j = abs (state);
	XSetForeground (display, gcpen, cbutton);
	
	XFillRectangle (display, buttonwindow, gcpen, xoffset + j,
			yoffset + j, xsize - 2 * j, ysize - 2 * j);
	
	XSetForeground (display, gcpen, cleft);
	
	XFillRectangle (display, buttonwindow, gcpen, xoffset, yoffset, xsize,
			j);
	
	XFillRectangle (display, buttonwindow, gcpen, xoffset, yoffset, j,
			ysize);
	
	XSetForeground (display, gcpen, cright);
	
	for (i = 0; i < j; i++){
		  XDrawLine (display, buttonwindow, gcpen,
			     xoffset + i, yoffset + ysize - i - 1,
			     xoffset + xsize - i - 1,
			     yoffset + ysize - i - 1);
		  XDrawLine (display, buttonwindow, gcpen,
			     xoffset + xsize - i - 1, yoffset + i,
			     xoffset + xsize - i - 1,
			     yoffset + ysize - i - 1);
	}
	
	if (NULL != text){
		  textlength = strlen (text);
		  XSetForeground (display, gcpen, ctext);
		  XDrawString (display, buttonwindow, gcpen,
			       xoffset + (xsize -
					  textlength * font_width) / 2,
			       yoffset + (ysize + font_height) / 2 - 2, text,
			       textlength);
	}
	
	return;
}

void	cleanup ()
{
	XUnloadFont (display, font->fid);
	XFreeGC (display, gc);
	XFreeGC (display, gcpen);
	XCloseDisplay (display);
	XDestroyImage (spinimage);
	if (NULL != field[0])
		free ((char *) field[0] - BPW);
	if (NULL != field[1])
		free ((char *) field[1] - BPW);
	exit (1);
}


/* from here on is the stuff for saving and restoring from a gif file */
/* for info on how gif works, see ftp://network.ucsd.edu/graphics/GIF.shar.Z */


char *picturename = "xsand.gif";

void addpic (data, xsize, ysize)	/* add in a GIF image to field  */
     unsigned char	*data;		/* where the output data starts */
     int		xsize, 
     			ysize;		/* output picture dimensions    */
/* load in a gif image */
{
	int		i,
			j,
			filesize,
			gwidth,
			gheight,
			gvolume;
	unsigned char	*ptr,
			*ptr1,
			*rawgif;
	int		colorbits,
			codesize;
	FILE		*infile;

	
	if (NULL == (infile = fopen (picturename, "r"))){
		  fprintf (stderr, "couldn't open input file\n");
		  return;
	}
	
	/* find the file size */
	fseek (infile, 0L, 2);
	filesize = ftell (infile);
	fseek (infile, 0L, 0);
	/* make a place in memory for the file */
	if (NULL == (rawgif = (unsigned char *) malloc (filesize))){
		  fprintf (stderr, "not enough memory to read gif file\n");
		  return;
	}
	
	ptr = rawgif;
	/* read in the file */
	if (fread (ptr, filesize, 1, infile) != 1){
		  fprintf (stderr, "read failed\n");
		  free ((char *) rawgif);
		  return;
	}
	
	fclose (infile);
	/* check for GIF signature */
	if (strncmp ((char *) ptr, "GIF87a", 6)){
		  fprintf (stderr, "not a GIF87a file\n");
		  free ((char *) rawgif);
		  return;
	}
	
	ptr += 6;
	ptr += 4;		/* skip over screen size  */
	colorbits = 1 + ((*ptr) & 0xf);	/* how many bits of color */
	ptr += 2;		/* skip over background   */
	
	if (*ptr++){	/* should be zero         */
		  fprintf (stderr, "corrupt GIF file\n");
		  free ((char *) rawgif);
		  return;
	}
	
	ptr += (3 * (1 << colorbits));	/* skip over colormap     */
	if (',' != (*ptr++)){	/* image separator        */
		  fprintf (stderr, "corrupt GIF file\n");
		  free ((char *) rawgif);
		  return;
	}
	
	ptr += 4;		/* skip over image offset */
	gwidth = (*ptr) + 0x100 * (*(ptr + 1));
	ptr += 2;
	gheight = (*ptr) + 0x100 * (*(ptr + 1));
	ptr += 2;
	
	if (0x80 & (*ptr++))	/* skip over local color map */
		ptr += (3 * (1 << colorbits));
	
	/* should now be at start of data */
	codesize = (*ptr++);
	/* make a place for the decompressed file */
	gvolume = gwidth * gheight;
	ptr1 = (unsigned char *) malloc (gvolume);
	decompress (codesize, ptr, ptr1, gvolume);
	free ((char *) rawgif);
	/* map picture into data, allowing for different dimensions */
	for (j = 0; j < ysize; j++){
		  if (j >= gheight)
			  break;
		  for (i = 0; i < xsize; i++){
			    if (i >= gwidth)
				    break;
			    data[i + j * xsize] += ptr1[i + j * gwidth];
		  }
	}
	
	free ((char *) ptr1);
	fixboundary ();
	return;
}


	
void	savepic (data, xsize, ysize)	/* save the field as a GIF image */
     unsigned char	*data;		/* where the input data starts   */
     int		xsize, 
     			ysize;	/* picture dimensions            */
{
	int	i;
	int	colorbits = 5,
		codesize = 5;	/* assume a 32 color image */
	FILE	*outfile;
	
	if (NULL == (outfile = fopen (picturename, "w"))){
		  fprintf (stderr, "couldn't open output file\n");
		  return;
	}
	
	/* GIF signature */
	fwrite ("GIF87a", 6, 1, outfile);
	/* screen descriptor */
	stringbuffer[0] = xsize & 0xff;	/* screen width  */
	stringbuffer[1] = (xsize >> 8) & 0xff;
	stringbuffer[2] = ysize & 0xff;	/* screen height */
	stringbuffer[3] = (ysize >> 8) & 0xff;
	stringbuffer[4] = (0x80)	/* M=1; global color map follows */
		| ((colorbits - 1) << 4)	/* -1+ bits of color reslution   */
		| (colorbits - 1);	/* -1+bits per pixel in image    */
	stringbuffer[5] = 0;	/* background color */
	stringbuffer[6] = 0;	/* should be zero   */
	fwrite (stringbuffer, 7, 1, outfile);
	/* global color map */
	
	for (i = 0; i < (1 << colorbits); i++){
		  colorcell.pixel = translate[i];
		  XQueryColor (display, cmap, &colorcell);
		  fputc (colorcell.red >> 8, outfile);
		  fputc (colorcell.green >> 8, outfile);
		  fputc (colorcell.blue >> 8, outfile);
	}
	
	/* image descriptor */
	stringbuffer[0] = ',';	/* image descriptor separator */
	stringbuffer[1] = 0;	/* image offset               */
	stringbuffer[2] = 0;
	stringbuffer[3] = 0;
	stringbuffer[4] = 0;
	stringbuffer[5] = xsize & 0xff;	/* image width  */
	stringbuffer[6] = (xsize >> 8) & 0xff;
	stringbuffer[7] = ysize & 0xff;	/* image height */
	stringbuffer[8] = (ysize >> 8) & 0xff;
	stringbuffer[9] = 0;	/* use global color map, no interlace  */
	fwrite (stringbuffer, 10, 1, outfile);
	/* start of image data */
	fputc (codesize, outfile);
	compress (codesize, data, outfile, volume);
	/* gif terminator */
	fputc (';', outfile);
	fclose (outfile);
	return;
}

/* LZW compression */
/* hash function assumes TABLELENGTH is a power of 2 */
# define TABLELENGTH (1<<13)
char **addresses = NULL;	/* where to find the string         */
int *codes = NULL,		/* the code value                   */
 *linktonext = NULL,		/* the next index in the hash chain */
 *lengths = NULL,		/* the length of the coded string   */
 *codeindex = NULL;		/* the index for a given code       */
int nextcode;			/* the next unused code             */

/* hashit is supposed to give a unique fairly random number in the table for
each length a and string b */

# define hashit(a,b) (51*a+53*(57*b[0]+59*(61*b[a-1]+b[a>>1])))&(TABLELENGTH-1)

void
compress (initcodesize, ptr, outfile, size)
     int initcodesize;		/* the initial compression bits */
     char *ptr;			/* where the data comes from    */
     FILE *outfile;		/* where the output goes        */
     int size;			/* how much data                */
{
	int currentcode, prefixcode = 0, codesize, maxbits = 12, maxcode;
	int clearcode, eoicode, currentplace = 0, length, blocksize =
		0, bitoffset;
	int findcode ();
	unsigned long outputword;
	unsigned char blockbuffer[256];	/* to hold data blocks before writing */
	/* allocate space for hash tables */
	if (NULL == (codes = (int *) malloc (sizeof (int) * TABLELENGTH)))
	  {
		  fprintf (stderr, "compress: trouble allocating tables\n");
		  currentplace = size;
	  }
	if (NULL ==
	    (linktonext = (int *) malloc (sizeof (int) * TABLELENGTH)))
	  {
		  fprintf (stderr, "compress: trouble allocating tables\n");
		  currentplace = size;
	  }
	if (NULL == (lengths = (int *) malloc (sizeof (int) * TABLELENGTH)))
	  {
		  fprintf (stderr, "compress: trouble allocating tables\n");
		  currentplace = size;
	  }
	/* need one extra place in codeindex for overflow before resetting: */
	if (NULL == (codeindex = (int *) malloc (sizeof (int) * 4097)))
	  {
		  fprintf (stderr, "compress: trouble allocating tables\n");
		  currentplace = size;
	  }
	if (NULL ==
	    (addresses = (char **) malloc (sizeof (char *) * TABLELENGTH)))
	  {
		  fprintf (stderr, "compress: trouble allocating tables\n");
		  currentplace = size;
	  }
	/* set up initial code table */
	inittable (initcodesize);
	clearcode = (1 << initcodesize);
	eoicode = clearcode + 1;
	codesize = initcodesize + 1;
	maxcode = 1 << codesize;
	nextcode = eoicode + 1;
	/* start with a clear code */
	outputword = clearcode;
	bitoffset = codesize;
	/* now do the compressing */
	while (currentplace < size)
	  {			/* check if codesize needs increasing */
		  if (nextcode > maxcode)
		    {
			    codesize++;
			    maxcode = 1 << codesize;
			    /* if too big, then reset compressor */
			    if (codesize > maxbits)
			      {
				      if (bitoffset)
					      outputword |=
						      (clearcode <<
						       bitoffset);
				      else
					      outputword = clearcode;
				      bitoffset += maxbits;
				      inittable (initcodesize);
				      codesize = initcodesize + 1;
				      maxcode = 1 << codesize;
				      nextcode = eoicode + 1;
			      }
		    }
		  /* look for an unstored string */
		  length = 1;
		  LZW (nextcode >
		       (currentcode =
			findcode (length, (char *) (ptr + currentplace))))
		  {
			  prefixcode = currentcode;
			  length++;
			  if ((currentplace + length) >= size)
				  break;
		  }
		  nextcode++;
		  currentplace += (length - 1);
		  /* output the prefix code */
		  if (bitoffset)
			  outputword |= (prefixcode << bitoffset);
		  else
			  outputword = prefixcode;
		  bitoffset += codesize;
		  /* output finished bytes to blocks */
		  while (bitoffset >= 8)
		    {
			    blockbuffer[blocksize] = outputword & 0xff;
			    outputword >>= 8;
			    bitoffset -= 8;
			    blocksize++;
			    /* output filled block */
			    if (blocksize >= 254)
			      {
				      fputc ((char) blocksize, outfile);
				      fwrite (blockbuffer, blocksize, 1,
					      outfile);
				      blocksize = 0;
			      }
		    }
	  }
	/* output the end of information code */
	if (bitoffset)
		outputword |= (eoicode << bitoffset);
	else
		outputword = eoicode;
	bitoffset += codesize;
	/* finish outputting the data */
	while (bitoffset >= 0)
	  {
		  blockbuffer[blocksize] = (char) (outputword & 0xff);
		  outputword >>= 8;
		  bitoffset -= 8;
		  blocksize++;
		  if (blocksize >= 254)
		    {
			    fputc ((char) blocksize, outfile);
			    fwrite (blockbuffer, blocksize, 1, outfile);
			    blocksize = 0;
		    }
	  }
	/* output the last block */
	if (blocksize)
	  {
		  fputc ((char) blocksize, outfile);
		  fwrite (blockbuffer, blocksize, 1, outfile);
	  }
	/* a final zero block count */
	fputc (0, outfile);
	/* deallocate tables */
	if (NULL != codes)
		free ((char *) codes);
	if (NULL != linktonext)
		free ((char *) linktonext);
	if (NULL != lengths)
		free ((char *) lengths);
	if (NULL != codeindex)
		free ((char *) codeindex);
	if (NULL != addresses)
		free ((char *) addresses);
	codes = linktonext = lengths = codeindex = NULL;
	addresses = (char **) NULL;
	return;
}

void
decompress (initcodesize, ptr, ptr1, size)
     int initcodesize;
     unsigned char *ptr, *ptr1;	/* compressed data from ptr go to ptr1 */
     int size;			/* an upper limit purely as a check    */
{
	int i, currentcode, codesize = 0, maxbits = 12, blocksize;
	int clearcode, eoicode, codemask = 0;
	int bitoffset = 0, indx, oldindx = 0;
	int currentplace = 0, oldplace = 0;
	int findcode ();
	unsigned long inputword = 0;
	unsigned char *p1, *p2;
	/* first deblock the data */
	p1 = p2 = ptr;
	blocksize = (*p1++);
	while (blocksize)
	  {
		  while (blocksize--)
			  (*p2++) = (*p1++);	/* a wonderful example of how abstruse C can be */
		  blocksize = (*p1++);
	  }
	/* set up initial code table */
	currentcode = clearcode = (1 << initcodesize);
	eoicode = clearcode + 1;
	/* allocate space for hash table */
	if (NULL == (codes = (int *) malloc (sizeof (int) * TABLELENGTH)))
	  {
		  fprintf (stderr, "decompress: trouble allocating tables\n");
		  currentcode = eoicode;
	  }
	if (NULL ==
	    (linktonext = (int *) malloc (sizeof (int) * TABLELENGTH)))
	  {
		  fprintf (stderr, "decompress: trouble allocating tables\n");
		  currentcode = eoicode;
	  }
	if (NULL == (lengths = (int *) malloc (sizeof (int) * TABLELENGTH)))
	  {
		  fprintf (stderr, "decompress: trouble allocating tables\n");
		  currentcode = eoicode;
	  }
	/* need one extra place in codeindex for overflow before resetting: */
	if (NULL == (codeindex = (int *) malloc (sizeof (int) * 4097)))
	  {
		  fprintf (stderr, "compress: trouble allocating tables\n");
		  currentcode = eoicode;
	  }
	if (NULL ==
	    (addresses = (char **) malloc (sizeof (char *) * TABLELENGTH)))
	  {
		  fprintf (stderr, "decompress: trouble allocating tables\n");
		  currentplace = eoicode;
	  }
	while (currentcode != eoicode)
	  {
		  if (currentcode == clearcode)	/* reset the decompressor */
		    {
			    inittable (initcodesize);
			    codesize = initcodesize + 1;
			    nextcode = eoicode + 1;
			    codemask = (1 << codesize) - 1;
			    oldindx = (-1);
		    }
		  else		/* code represents data */
		    {
			    indx = codeindex[currentcode];	/* where in table is currentcode */
			    if (indx >= 0)	/* it is there */
			      {	/* put it into the output */
				      for (i = 0; i < lengths[indx]; i++)
					      ptr1[currentplace + i] =
						      addresses[indx][i];
				      if (oldindx >= 0)	/* first character treated differently */
					{
						findcode (lengths[oldindx] +
							  1,
							  (char *) (ptr1 +
								    oldplace));
						nextcode++;	/* add new code to table */
					}
				      oldplace = currentplace;
				      currentplace += lengths[indx];
				      oldindx = indx;
			      }
			    else	/* not in table yet; must be old code plus last=first character */
			      {
				      for (i = 0; i < lengths[oldindx]; i++)
					      ptr1[currentplace + i] =
						      addresses[oldindx][i];
				      ptr1[currentplace + lengths[oldindx]] =
					      addresses[oldindx][0];
				      /* store new code */
				      findcode (lengths[oldindx] + 1,
						(char *) (ptr1 +
							  currentplace));
				      oldplace = currentplace;
				      currentplace += lengths[oldindx] + 1;
				      oldindx = codeindex[nextcode++];
			      }
			    /* crude error checking */
			    if ((oldindx < 0) || (currentplace > size))
			      {
				      fprintf (stderr,
					       "gif file appears to be corrupt\n");
				      break;
			      }
		    }
		  /* check if codesize needs increasing */
		  if (nextcode > codemask)
			  if (codesize < maxbits)
			    {
				    codesize++;
				    codemask = (1 << codesize) - 1;
			    }
		  while (bitoffset < codesize)	/* read some more data */
		    {
			    if (bitoffset)
				    inputword |=
					    (((int) (*ptr++)) << bitoffset);
			    else
				    inputword = (*ptr++);
			    bitoffset += 8;
		    }
		  /* strip off current code */
		  currentcode = inputword & codemask;
		  inputword >>= codesize;
		  bitoffset -= codesize;
		  if (currentcode > nextcode)
		    {
			    fprintf (stderr,
				     "gif file appears to be corrupt\n");
			    break;
		    }
	  }
	/* deallocate tables */
	if (NULL != codes)
		free ((char *) codes);
	if (NULL != linktonext)
		free ((char *) linktonext);
	if (NULL != lengths)
		free ((char *) lengths);
	if (NULL != codeindex)
		free ((char *) codeindex);
	if (NULL != addresses)
		free ((char *) addresses);
	codes = linktonext = lengths = codeindex = NULL;
	addresses = (char **) NULL;
	return;
}

void
inittable (size)
     int size;
{
	int i, findcode ();
	for (i = 0; i < TABLELENGTH; i++)
	  {
		  linktonext[i] = (-1);
		  codes[i] = (-1);
		  lengths[i] = (-1);
	  }
	for (i = 0; i < 4096; i++)
		codeindex[i] = (-1);
	/* store initial codes for raw characters */
	nextcode = 0;
	for (i = 0; i < (1 << size); i++)
	  {
		  stringbuffer[i] = i;
		  findcode (1, (char *) (stringbuffer + i));
		  nextcode++;
	  }
	return;
}

int
findcode (length, string)
     char *string;
     int length;
 /* return code for string of given length;  
    if not found, store it and return nextcode */
{
	int i, j, indx, previousindex;
	indx = hashit (length, string);
	/* look for string in table */
	previousindex = indx;
	while (indx > 0)
	  {
		  if (lengths[indx] == length)	/* is the length right ? */
		    {
			    for (j = 0; j < length; j++)
				    if ((string[j]) != (addresses[indx][j]))
					    break;
			    if (j == length)
				    return codes[indx];
		    }
		  previousindex = indx;
		  indx = linktonext[indx];
	  }
	/* not found, so store it */
	indx = previousindex;
	i = indx;
	while (codes[i] >= 0)	/* find an unused slot in table */
	  {
		  i++;
		  if (i >= TABLELENGTH)
			  i -= TABLELENGTH;
	  }
	if (i != indx)
	  {
		  linktonext[indx] = i;	/* link to it */
		  indx = i;	/* move to it */
	  }
	codes[indx] = nextcode;	/* save the new code */
	lengths[indx] = length;
	addresses[indx] = string;
	codeindex[nextcode] = indx;
	return nextcode;
}

