/*
 *   xfroot - set root window to monochrome fractal
 *
 *   xfroot [-p npoints] [-P npoints] [-a value] [-b value] [-c value]
 *          [-display display]
 *
 *   -p npoints: maximum in-range points to calculate          
 *   -P npoints: maximum total points to calculate             
 *               defaults: -p: 25% of pixels in display 
 *                         -P: 3 * -p value
 *   value:   floating point values for hopalong a, b, and c parameters
 *            (default: randomly selected)
 *     
 *   The fractal algorithm (hopalong) was adapted from A. K. Dewdney's 
 *   "Computer Recreations" in the 9/86 Scientific American (attributed 
 *   to Barry Martin of Aston University, Birmingham, England.)
 *
 *   The Xlib business-end (setroot) was adapted from "xphoon" by 
 *   Jef Poskanzer and Craig Leres. 
 *
 *   The Ranf portable random number generator is based on work by
 *   D. H. Lehmer, Knuth, David Sachs(Fermilab) and Curt Canada(NCSA).
 *
 *   This software is provided "as is" and with no warranties of any kind.
 *
 *   Ed Kubaitis
 *   Computing Services Office
 *   University of Illinois, Urbana
 */


#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include <sys/times.h>

#define HZ  60      /* see REAMDME */

#define Ranf() (double)((Ranfseed=(Ranfseed*1629+1)%1048576)/1048576.)
#define Ranfset(l) (Ranfseed=(((abs(l)%1048576)*1629)+1)%1048576)
long    Ranfseed=4326;

int     mxp=0, np=0;       /* max in-range points, in-range points */
int     mxP=0, nP=0;       /* max total points,    total points    */
double  A=0, B=0, C=0;     /* hopalong parameters                  */
int     W, H;              /* width and height of display (pixels) */
char    *bmap;             /* bitmap                               */
int     bmn;               /* number of bytes in bitmap            */
Display *dpy = NULL;       



main(argc, argv) int argc; char *argv[]; {
   struct tms t0, t1;
   int ticks;

   preset(argc, argv);

   times(&t0);
   hopalong(); 
   times(&t1);

   ticks = (t1.tms_utime + t1.tms_stime) - (t0.tms_utime + t0.tms_stime);
   printf("xfroot: %d points   %d(%d%%) in-range   %d points/cp_second\n",
	  nP, np, (100*np)/nP, 
          (int)((double)nP/((double)ticks/(double)HZ)));

   setroot(W,H,bmap);
   XCloseDisplay(dpy);
   exit(0);
   }


hopalong() {
   int wc=W/8, cx=W/2, cy=H/2, ix, iy; 
   double x=0, y=0, xx, yy, t;

   while (np < mxp && ++nP < mxP) {
      t = sqrt(fabs(B*x-C));
      xx = y - ( (x<0) ? t : -t );
      yy = A - x;
      x = xx; y = yy;
      ix = cx + x; iy = cy + y;
      if (ix>-1 && iy>-1 && ix<(W-1) && iy<(H-1)) {
 	 bmap[iy*wc+(ix>>3)] |= 1<<(ix&7);
	 np++;
	 }
      }
   }


hopset() {
   int r = Ranfseed; double pmax=1.0*W, logpmax=log(pmax);
   if (!A) { A = exp(Ranf()*logpmax); if (r&2) A = -A; }
   if (!B) { B = exp(Ranf()*logpmax); if (r&4) B = -B; }
   if (!C) { C = Ranf()*pmax;         if (r&8) C = -C; }
   }


preset(argc, argv) int argc; char *argv[]; {
   char *display = NULL;       
   int  i;

   for (i=1; i<argc; i++) {
      if (!strcmp(argv[i], "-p")) {
	 if (++i>argc) usage();
	 if(!sscanf(argv[i], "%d", &mxp)) usage();
	 if (mxp <= 0) usage();
	 }
      else if (!strcmp(argv[i], "-P")) {
	 if (++i>argc) usage();
	 if(!sscanf(argv[i], "%d", &mxP)) usage();
	 if (mxP <= 0) usage();
	 }
      else if (!strcmp(argv[i], "-a")) {
	 if (++i>argc) usage();
	 if(!sscanf(argv[i], "%lf", &A)) usage();
	 }
      else if (!strcmp(argv[i], "-b")) {
	 if (++i>argc) usage();
	 if(!sscanf(argv[i], "%lf", &B)) usage();
	 }
      else if (!strcmp(argv[i], "-c")) {
	 if (++i>argc) usage();
	 if(!sscanf(argv[i], "%lf", &C)) usage();
	 }
      else if (!strcmp(argv[i], "-display")) {
	 if (++i>argc) usage();
	 display=argv[i];
	 }
      else 
	 usage();
      }

   dpy = XOpenDisplay(display); 
   if (!dpy) { 
      fprintf(stderr, "xfroot: Can't open display '%s'.\n", display); 
      exit(1); 
      }

   W = (int) DisplayWidth(dpy,0);
   H = (int) DisplayHeight(dpy,0);
   bmn =((W+7)/8)*H;
   bmap = (char *) malloc((unsigned)bmn);
   if (!bmap) { fprintf(stderr, "xfroot: malloc failed.\n"); exit(1);}
   bzero(bmap, bmn);

   Ranfset(time(0)); hopset(); 

   if (!mxp && !mxP) {
      mxp = (int) (0.25 * (float)(W*H));
      mxP = 3 * mxp;
      }
   else if (!mxp)
      mxp = mxP;
   else if (!mxP)
      mxP = 3 * mxp;

   fprintf(stderr, "xfroot: A:%f B:%f C:%f\n", A, B, C);
   }



/*
** Copyright (C) 1988 by Jef Poskanzer and Craig Leres.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/
setroot(w, h, bits)
	int w, h;
	char *bits;
{
	Pixmap bitmap, pixmap;
	GC gc;
	XGCValues gcv;

	bitmap = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), bits, w, h);
	if (bitmap == 0) {
		fprintf(stderr, "%s: Unable to store Bitmap", "xfroot");
		exit(1);
	}
	gcv.foreground = WhitePixel(dpy,DefaultScreen(dpy));
	gcv.background = BlackPixel(dpy,DefaultScreen(dpy));
	gc = XCreateGC(
	    dpy, DefaultRootWindow(dpy), GCForeground|GCBackground, &gcv);

	pixmap = XCreatePixmap(
	    dpy, DefaultRootWindow(dpy), w, h,
	    DefaultDepth(dpy, DefaultScreen(dpy)));
	if (pixmap == 0) {
		fprintf(stderr, "%s: Unable to create Pixmap", "xfroot");
		exit(1);
	}

	XCopyPlane(dpy, bitmap, pixmap, gc, 0, 0, w, h, 0, 0, 1L);
	XSetWindowBackgroundPixmap(dpy, DefaultRootWindow(dpy), pixmap);
	XFreeGC(dpy, gc);
	XFreePixmap(dpy, bitmap);
	XFreePixmap(dpy, pixmap);
	XClearWindow(dpy, DefaultRootWindow(dpy));
	XFlush(dpy);

}

usage() { 
   fprintf(stderr, "usage: xfroot ");
   fprintf(stderr, "[-p npoints] "); 
   fprintf(stderr, "[-P npoints] "); 
   fprintf(stderr, "[-a real] "); 
   fprintf(stderr, "[-b real] "); 
   fprintf(stderr, "[-c real] "); 
   fprintf(stderr, "[-display display] "); 
   fprintf(stderr, "\n"); 
   exit(1); 
   }
