/* erode.c:  read in array of elevation values              */
/*           generate array of directions, flows etc.       */
/*           John P. Beale 6/10/95-7/7/95                   */

#include "erlib.h"
#include "erode.h"
#include "inout.h"

#define EDGELEN         1000.0  /* image size (bottom edge length) in meters */
#define MAXALT          100.0   /* meters elevation between 0.0 and 1.0 */
#define MAXSLOPE        1.5       /* maximum slope in radians (ngl of repose) */
#define MINSLOPE        1.0     /* slope in lowlands */

int fillb(int i);               /* fill_bn, i times */
void slump(double rate);                   /* mudslide algorithm */
/* int slump_1(int sl, int i, PTYPE *hf1, PTYPE *hf2); */

void erode();                   /* water erosion */
void erode_1(PTYPE *hf1, PTYPE *hf2);     /* called by erode() */
int correct_1(PTYPE *hf1, PTYPE *hf2);   /* fix over-erosions */
double smooth(float f);           /* smooth hf array an amount f (0..1) */
void rescale();                 /* rescale hf matrix to 0..1 */
double sgn1(double a);          /* sign of argument */

int lowestn(PTYPE *hf, int x, int y, float *m); /* dir. of steepest descent */
double errate = 2e-4;          /* rate of erosion */
double er_thresh = 2.0;          /* elevation above which no erosion happens */
double er_sedfac = 1.6;          /* slope ratio thresh. for sedimentation */
double pow_fac = 0.0;            /* power of altititude in smoothing rate */
double er_pow = 1.0;             /* power of flow in erosion rate */
double pow_offset = 1.0;         /* constant term in erosion rate */
double er_debug = 0.5;

/* -------------------------------------------------------------- */
/*   main() --  get command-line args or go into interactive mode */
/*              call routines to do stuff.                        */
/* -------------------------------------------------------------- */

int main(int argc, char **argv)
{
char *usage = "Erode v0.1 (c) John Beale <beale@jump.stanford.edu> 7/15/95\n\
Usage:\n\
erode <infile> [fillb er_cyc er_rate er_pow sm_rate ert est pfac poff erd]\n\
   Defaults: fillb 300  er_cycles 10  er_rate 2e-4 er_pow 1.0\n\
	     sm_rate 0.05  ert 2.0  est 1.6 pfac 0.0 poff 1.0 erd 0.5\n\
   ------------------------------------------------------------------\n\
   You only need specify as many parameters as you want to change; the\n\
   rest will be set to their defaults. However they must be specified in\n\
   order. Example:    erode output.mat 100 5 5e-3\n\
   --------------------------------------------------------------------\n\
   fillb: number of iterations used in initial basin-filling step\n\
   er_cycles: number over overall erosion iteration cycles to do\n\
   er_rate: maximum elevation subtracted each iteration\n\
   er_pow: exponent of flow rate in erosion equation\n\
   sm_rate: smoothing factor, 1.0 = replace pixel with avg. of neighbors\n\
   ert: erosion threshold, altitude above which no erosion at all occurs\n\
   est: erosion/sedimentation slope difference changeover threshold\n\
   pfac: exponent of smoothing-rate dependence on altitude\n\
   poff: constant term in smoothing rate dependence on altitude\n\
   erd: erosion algorithm debug value, print out erosion rates g.t. erd\n\
   -------------------------------------------------------------------- \n\
   Erode generates three output files: topo.mat, delta.mat, river.gif\n";
  char fname_in[160];
  char fname_out[60];
  char *buf;
  int tmp, i, j,saves;
  int imax = 100; /* # of basin fills */
  int ecycles = 10;  /* erosion cycles */
  float smrate = 0.05;  /* rate of smoothing between erosions */
  int rval=0;

  if ((argc > 12) || (argc < 2)) {
	fprintf(stderr,"%s",usage);
	exit(1);
  } else {
	strcpy(fname_in,argv[1]);
	if (argc > 2) {
	imax = (int) strtod(argv[2] , &buf);
	}
	if (argc > 3) { 
	   ecycles = (int) strtod(argv[3] , &buf);
	}
	if (argc > 4) {
	   errate = (float) strtod(argv[4], &buf);
	}
	if (argc > 5) {
	   er_pow = (float) strtod(argv[5], &buf);
	}
	if (argc > 6) {
	   smrate = (float) strtod(argv[6], &buf);
	}
	if (argc > 7) {
	   er_thresh = (float) strtod(argv[7], &buf);
	}
	if (argc > 8) {
	   er_sedfac = (float) strtod(argv[8], &buf);
	}
	if (argc > 9) {
	   pow_fac = (float) strtod(argv[9], &buf);
	}
	if (argc > 10) {
	   pow_offset = (float) strtod(argv[10], &buf);
	}
	if (argc > 11) {
	   er_debug = (float) strtod(argv[11], &buf);
	}
  }

   printf("imax %d ecyc %d errate %1.1e pow %1.1e smrate %1.1e ert %1.1e\n",
		 imax,ecycles,errate,er_pow,smrate,er_thresh);
   printf("est %1.1e pow_fac %1.1e  pow_offset %1.1e  er_debug %1.1e\n",
		er_sedfac, pow_fac, pow_offset, er_debug);
   rval = hf_read(fname_in);    /* read in heightfield */
   if (rval != 0) exit(rval);
   rescale();                  /* make it 0..1 */

   printf("%s xsize ysize = %d %d\n",fname_in,xsize,ysize); 
   if (xsize < 20) {
      hf_print();  /* print small arrays */
   }
   /* slump(20); */

   rval = alloc_mem();          /* alloc memory for fl, flag, ua arrays */
   if (rval != 0) exit(rval);

   /* printf("xsize %d  ysize %d \n",xsize,ysize); */


/*   find_upflow();   */
				/* set flow array */
     /* quick_flow(); */
   find_flow2();
/*   fl_print();
   flag_print(); */
   find_upflow();  

   if (xsize < 32) {
      fl_print();
      fl_p2();
     
     flag_print(); 
   }
   tmp = 1;
   printf("Filling basins (maxiter %d)...\n",imax);

   i = 0;
   saves = 0;

   /* -----------------        FILL BASINS !!  --------------------- */
   fillb(imax);
   
   printf("basin fill:\n");
   if (xsize < 20) ar_print(bn);
    
   printf("finding flow...\n");
   find_flow2();        /* set fl (downhill-pointers) array */
   find_upflow();       /* set flow (uphill-pointers) array */
     if (xsize < 32) {         /* print out mx */
	fl_p2();
	fl_p3();     /* print flag (uphill neighbor) array */
     }
   check_con(); /* check consistency of peaks with flow map */
   find_ua();   /* solve for cumulative flow */
   
   printf("eroding %d cycles... \n",ecycles);
   for (i=0;i<ecycles;i++) {
    printf("erosion iter %d\n",i+1);
    for (j=0;j<5;j++) {
      erode();            /* erode riverbeds */
      slump(smrate);      /* slump sidewalls (mudslide) */
      fillb(10);          /* fill any inadvertently-created basins */
/*      sm_scale = smooth(smrate);     
      printf("sm:%1.1e ",sm_scale);  */
      find_flow2();       /* find new flowlines */
      find_upflow();
      find_ua();
    }
    /* write topo as Matlab file */
    /* rval = write_mfile1("topo.mat","land",hf,xsize,ysize); */ 
    /* rval = write_mfile("delta.mat","delta",bn,xsize,ysize);*/ /* what's changed */
    sprintf(fname_out,"topo%02d.pgm",i);
    (void) pgm_write_hf(fname_out,hf);
    sprintf(fname_out,"uphill%02d.pgm",i);
    (void) pgm_write(fname_out,ua);
   }

   if (xsize+2 < 20) {
     ar_print(ua);
     fl_print();
     flag_print();
   }

   if (ecycles < 1) {   /* write out data even if no erosion */
    rval = write_mfile1("topo.mat","land",hf,xsize,ysize);  
    rval = write_mfile("delta.mat","delta",bn,xsize,ysize); /* what's changed */
   }
   rval = gif_vwrite("river.gif",ua);    /* flow map */

  return(rval);    /* return a value */

}  /* end main() ---------------------------------------------  */

/* ----------------------------------------------------------   */
/* fillb   --   call fill_bn() (fill basins) up to imax times   */
/*              in order to fill in basins, returns # of calls  */
/* ----------------------------------------------------------   */

int fillb(int imax)
{
 int icount = 0;
 int tmp = 1;
 int i=0;
 int sc = 0;
 PTYPE *hf2;

 /* allocate temporary storage array */
 hf2 = (PTYPE *) malloc((size_t)sizeof(PTYPE)*(xsize+2)*(ysize+2));
   if (hf2 == NULL) {
     fprintf(stderr,"Could not allocate hf buffer memory.\n");
     exit(1);
   }
   while ((tmp > 0) && (icount<imax) ) {
     quick_flow(hf);              /* find local minima only */
     tmp = fill_bn(hf, hf2);    /* temp <- original */
     printf("%d ",tmp);
     if (++i > 5) {
       i = 0;
       printf("\n");
     }
     icount++;
     quick_flow(hf2);              /* find local minima only */
     tmp = fill_bn(hf2, hf);    /* original <- temp */
     printf("%d ",tmp);
     if (++i > 5) {
       i = 0;
       printf("\n");
     }
     icount++;

     fflush(stdout);
     /* find_upflow(); */
     if (xsize+2 < 30) {         /* print out mx */
	fl_p2();
     }
     if (++sc > 50) {           /* save intermed. result every 100 fills */
       write_mfile1("topo.mat","land",hf,xsize,ysize);  
       sc = 0;
     }
   } /* end while  */
   printf("\n");

   free(hf2);
   return(icount);
   printf("fillb finished\n");
}


/* ------------------------------------------------------------ */
/*    find_ua()  --  find uphill area for each mx element       */
/* ------------------------------------------------------------ */
void find_ua()                /* find uphill area */
{
int ix,iy;
int i;
int dir;              /* direction this element points in */
BYTE ff;               /* flag variable for this loc */
int o_summed;         /* flag indicating all neighbors summed */
long added;            /* # found unsummed nodes this pass */
float area;           /* sum of uphill area for this element */


/* first pass: set local highpoints to "summed", area to 1 */

 for (iy = 0; iy<ysize; iy++) {
    for (ix = 0; ix<xsize; ix++) {
      if ( (El(flag,ix,iy) & 0x10) == 0x10) { /* pre-marked summed (no inflows) */
	El(ua,ix,iy) = 1;
	/* printf("presummed %d %d\n",ix,iy); */
      }
      if ( (El(flag,ix,iy) & 0x0f) == 0) {   /* this point is a peak */
	El(ua,ix,iy) = 1;                     /* peak: unit area */
	El(flag,ix,iy) = (El(flag,ix,iy) | 0x10);  /* set b4: node summed */
      } /* end if */
    } /* end for ix */
 } /* end for iy */

/* 2nd through n passes: cumulative add areas in a downhill-flow hierarchy */
 added = 1;
 while ( added > 0 ) {
   added = 0;
   for (iy = 1; iy<ysize-1; iy++) {
    for (ix = 1; ix<xsize-1; ix++) {
      ff = El(flag,ix,iy);
      if (!(ff & 0x10)) {      /* flag bit b4: set = ua for this node done */
	/* if all neighbors flowing into this node done, can sum */
	o_summed = 1;           /* start assuming we're ok... 0=false*/
	for(i=1;i<9;i++) {      /* check if relevant neighbors finished */
	  dir = El(fl,ix+xo[i],iy+yo[i]);
	  if ( ((xo[dir]+xo[i])==0) && ((yo[dir]+yo[i])==0))
		/* ie,that node points here */
		if (!(El(flag,ix+xo[i],iy+yo[i]) & 0x10))
		   o_summed = 0;
	} /* end for i=1..8 */
	if (o_summed) {
	  area = 1;
	  for (i=1;i<9;i++) {
	    dir = El(fl,ix+xo[i],iy+yo[i]);
	    if ( ((xo[dir]+xo[i])==0) && ((yo[dir]+yo[i])==0))
	      area += El(ua,ix+xo[i],iy+yo[i]);
	    }  /* end for i=1..9 */
	  El(ua,ix,iy) = area;
	  El(flag,ix,iy) = El(flag,ix,iy) | 0x10;  /* set b4: node summed */
	  added++;  /* increment count of nodes summed */
	}  /* end if o_summed */

      } /* end if b4 set */
    }  /* end for x=1..xsize-1 */
   } /* end for y=1..ysize-1 */

   /* printf("before edges, added = %d ",added); */
   /* aaand now... do the edges */
   for (iy = 0; iy<ysize; iy++) {
      added += ua_iter(0,iy);
      added += ua_iter(xsize-1,iy);
      }
   for (ix = 1; ix<xsize-1; ix++) {
	added += ua_iter(ix,0);
	added += ua_iter(ix,ysize-1);
      }

   /* printf("after %d, hit enter...\n",added); */

  } /* end while */

}  /* end find_ua()  */

/* --------------------------------------------------------- */

int ua_iter(int ix, int iy)   /* one iteration of uphill-area +bounds checks */
{
int i;
int xx, yy;
int tmp;
int dir;              /* direction this element points in */
BYTE ff;               /* flag variable for this loc */
int o_summed;         /* flag indicating all neighbors summed */
int added=0;          /* if this node was added */
float area;           /* sum of uphill area for this element */


   ff = El(flag,ix,iy);
  /* printf("(%d,%d:%u)",ix,iy,ff); */
   if ((ff & 0x10) != 0x10) { /* flag bit b4: set = ua for this node done */
	/* printf("!"); */
	/* if all neighbors flowing into this node done, can sum */
	o_summed = 1;           /* start assuming we're ok... 0=false*/
	for(i=1;i<9;i++) {      /* check if relevant neighbors finished */
	 xx = ix+xo[i];
	 yy = iy+yo[i];
	 if ((xx >= 0) && (xx <= xsize) && (yy >= 0) && (yy <= ysize)) {
	  dir = El(fl,xx,yy);
	  if ( ((xo[dir]+xo[i])==0) && ((yo[dir]+yo[i])==0))
		/* ie,that node points here */
		if (!(El(flag,xx,yy) & 0x10))
		   o_summed = 0;
	 } /* end if xx,yy inbounds */
	} /* end for i=1..8 */
	if (o_summed) {
	  area = 1;
	  for (i=1;i<9;i++) {
	   xx = ix+xo[i];
	   yy = iy+yo[i];
	   if ((xx >= 0) && (xx <= xsize) && (yy >= 0) && (yy <= ysize)) {
	    dir = El(fl,xx,yy);
	    if ( ((xo[dir]+xo[i])==0) && ((yo[dir]+yo[i])==0))
	      area += El(ua,xx,yy);
	   } /* end if xx,yy inbounds */
	  }  /* end for i=1..9 */
	  El(ua,ix,iy) = area;
	  tmp = El(flag,ix,iy);
	  El(flag,ix,iy) = tmp | 0x10;  /* set b4: node summed */
	  added = 1;
	  /* printf("ea %d,%d ",ix,iy); */
	}  /* end if o_summed */

   } /* end if b4 set */
   return(added);       /* return 1 if this node was summed, 0 otherwise */
}

/* ------------------------------------------------------------ */
/* check_con  --  check consistency of peak with flow map       */
/*                check b4 of flag array                        */
/* ------------------------------------------------------------ */

void check_con()
{
  int ix,iy;
  int i, dir;
  int d;
  int peaks=0;
  int summed=0;

   for (iy = 1; iy<(ysize-1); iy++) {  /* skip the edges */
    for (ix = 1; ix<(xsize-1); ix++) {
      if ( (El(flag,ix,iy) & 0x10) == 0x10) summed++;

      d = El(flag,ix,iy);
      if (d==0) {       /* supposed to be peak */
       peaks++;
       for(i=1;i<9;i++) {
	 dir = El(fl,ix+xo[i],iy+yo[i]);
	 if ((xo[i]+xo[dir]==0) && (yo[i]+yo[dir]==0)) {
	   printf("%d,%d:%1.4e flow from %d,%d:%1.4e\n",
	    ix,iy,Ehn(hf,ix,iy),ix+xo[i],iy+yo[i],Ehn(hf,ix+xo[i],iy+yo[i]));
	 }
       } /* end for i */
      } /* end if d==0 */
    } /* end for ix */
   } /* end for iy */
   printf("\nCons. check excl. edges: %d peaks, %d summed.\n",peaks,summed);
}

/* ------------------------------------------------------------ */
/* erode  --  simulate erosion by shifting material in hf[]     */
/*            downhill depending on slope and stream flow       */
/*              depends on ua (uphill area) being set           */
/* ------------------------------------------------------------ */

void erode()
{ 
PTYPE *hf2;
int fixed;

 hf2 = (PTYPE *) malloc((size_t)sizeof(PTYPE)*(xsize+2)*(ysize+2));
   if (hf2 == NULL) {
     fprintf(stderr,"Could not allocate hf buffer memory.\n");
     exit(1);
   }
  printf("Erode...");
  fflush(stdout);
  erode_1(hf, hf2);
  fixed = correct_1(hf2, hf);
  printf("%d fixed\n",fixed);

 free(hf2);
} /* end erode() */

/* ------------------------------------------------------------------- */
/* erode_1()  --  erode one step, taking input hf1[] into output hf2[] */
/* ------------------------------------------------------------------- */

void erode_1(PTYPE *hf1, PTYPE *hf2)
{
/* Erodef(slope,flow)  is the function controlling erosion rate */
/*                               slope exponent   flow exponent */
#define Erodef(a,b)  sgn1(a) * pow(fabs(atan(a)),1.4) * pow((b),0.8)
  
  int ix,iy,i,dir=0;              /* indexes into arrays */
  int upd;                       /* direction of uphill neighbor */
  double inslope, outslope;      /* product of slope and flow_rate */
  double outflow;                /* flow out of this cell */
  double erval;                  /* erosion potential at this point */
  int x1,y1;
  double slopefactor;            /* correction factor to scale slope */
  double h, dh, slope;  /* dh- delta height  dr- horizontal travel */

  slopefactor = MAXALT / (EDGELEN / xsize);  /* slope of 0.0 next to 1.0 */

  /* recall that hf1,hf2 are arrays [0..xsize+1, 0..ysize+1]   */
  for (iy=0;iy<(ysize+2);iy++) {      /* copy edges of hf1 (-eps) into hf2*/
     h = Eh(hf1,0,iy) - errate; if (h<0) h=0;
     Eh(hf2,0,iy) = h;
     h = Eh(hf1,xsize+1,iy) - errate; if (h<0) h=0;
     Eh(hf2,xsize+1,iy) = h;
  }
  for (ix=1;ix<(xsize+1);ix++) {
     h = Eh(hf1,ix,0) - errate; if (h<0) h=0;
     Eh(hf2,ix,0) = h;
     h = Eh(hf1,ix,ysize+1) - errate; if (h<0) h=0;
     Eh(hf2,ix,ysize+1) = h;
   }

   for (iy = 0; iy<ysize; iy++) {  /* skip the edges */
    for (ix = 0; ix<xsize; ix++) {
     h = Ehn(hf1,ix,iy);        /* altitude at this point ix,iy */
     if (h < er_thresh) {
      inslope = 0;       /* find sum of inflowing neighbors (slope*flow) */
      for (i=1;i<9;i++) {               /* loop over neighbors of ix,iy */
       x1 = ix+xo[i]; 
       y1 = iy+yo[i];
				/* check if off edge of array */
       if ((x1>=0) && (y1>=0) && (x1<xsize) && (y1<ysize)) {
	dir = El(fl,x1,y1);   /* direction index of this neighbor */
	if ((xo[i]+xo[dir]==0) && (yo[i]+yo[dir]==0)) { /* inflow from here */
	  slope = Ehn(hf1,x1,y1) - Ehn(hf1,ix,iy); /* pos = neigh. higher */
	  slope *= slopefactor;                 /* scale to corr. units */
	  if ((i==1) || (i==3) || (i==5) || (i==7))
	    slope = slope / 1.414;      /* diagonal neighbor; reduce slope */
	  inslope += Erodef(slope,(double)El(ua,x1,y1));

	 } /* end if ((xo... */
       } /* end if x1>=0 ... */
      } /* end for i */         /* now we've got sum of (slope*flow) */

      /* dir = lowestn(hf1,ix,iy,(float *)&slope);  direction of outflow */
      dir = El(fl,ix,iy);       /* direction of outflow */
					/* slope positive: neighbor lower */
      if (dir > 0) { /* if not a minimum... */
       h = Ehn(hf1,ix,iy);      /* current elevation at this point */
       if (h > 1.0) {
	 fprintf(stderr,"! h(%d,%d)=%1.3f\n",ix,iy,h);
	 h = 1.0;
       }
       slope = h - Ehn(hf1,ix+xo[dir],iy+yo[dir]); 
       slope *= slopefactor;
	   if ((dir==1) || (dir==3) || (dir==5) || (dir==7))
	     slope = slope / 1.414;      /* diagonal neighbor; reduce slope */
       outflow = (double) El(ua,ix,iy);

       if (outflow<1) outflow = 0.9;
       outslope = Erodef(slope,outflow);
	      
	/* positive dh = erosion happens, neg = sedimentation */
	/* sedimentation when (inslope) greater than (er_sedfac*outslope) */

       erval = errate * (er_sedfac*outslope - inslope); 
       erval *= pow(outflow,er_pow);
       dh = errate * erval; 
       h = Ehn(hf1,ix,iy);      /* old altitude at this point */

       if (dh > er_debug) {  /* print debugging info */
    printf("(%d,%d): dh=%1.1e %0.2f ins=%1.1e outs=%1.1e sl=%1.1e fl=%1.1e\n",
		ix,iy,dh,h,inslope,outslope,slope,outflow);
       }
       if (dh > errate) dh = errate;   /* don't erode too fast */
       if  ( (h-dh) < 0 ) {  /* don't let altitudes go negative */
	  dh = -h;           /* min elevation will be == 0.0 */
	}
		/* don't sediment too fast... */
		/* this should really depend on neighboring elevations */
       if (dh < -(0.03*errate)) dh = -(0.03*errate); 
				   
				   /* ------------------------------- */
				   /* the actual erosion. right here! */
				   /* ------------------------------- */
       Ehn(hf2,ix,iy) = h - dh; 
       El(bn,ix,iy) -= dh;       /* accumulate erosion rate separately */      

      } else {  /* if point is a local minimum (oops!) */
	upd = El(flag,ix,iy) & 0x0f;   /* direction of uphill neighbor */
	Ehn(hf2,ix,iy) = Ehn(hf1,ix+xo[upd],iy+yo[upd])+0.00001;
      } /* end if (dir>0) */
     } else {  /* NO erosion if point above erosion threshold */
	Ehn(hf2,ix,iy) = Ehn(hf1,ix,iy);
     } /* end if (h < er_thresh) */
    } /* end for ix */
   } /* end for iy */

} /* end erode_1() (whew!) */


/* --------------------------------------------------------------------- */
/* correct_1()  --      Input hf1, output hf2.  Checks that every point  */
/*                      in hf1 has an output flow (from fl[]) that is    */
/*                      actually downhill, and if it isn't, makes it so. */
/*                      Does not set the border pixels in hf2[].         */
/* --------------------------------------------------------------------- */

int correct_1(PTYPE *hf1, PTYPE *hf2)
{
int ix, iy;
int dir;
int fixed;              /* number of pixels with altitude adjusted */
PTYPE h, hdown;
   
  fixed = 0;
  for (iy = 0; iy < ysize; iy++) {
   for (ix = 0; ix < xsize; ix++) {
    h = Ehn(hf1,ix,iy);         /* elevation at this point */
    dir = El(fl,ix,iy);         /* flow direction */
    hdown = Ehn(hf1,ix+xo[dir],iy+yo[dir]);   /* neighbor receiving flow */
    if (hdown > h) {            /* oops, flow is uphill! we over-eroded */
      /* so, this pixel -> just a tad higher than the downflow neighbor */
      Ehn(hf2,ix,iy) = (0.0001 + hdown);
      El(bn,ix,iy) += (0.0001 + hdown - h);
      fixed++;
    } else {                    /* otherwise, just copy over to hf2 */
      Ehn(hf2,ix,iy) = h;
    }
   } /* end for ix */
  } /* end for iy */
  return(fixed);
} /* end correct_1 */


/* ------------------------------------------------------------ */
/* smooth  --  simulate random smoothing (raindrops?) in hf[]   */
/*             not linear; points move down faster than up      */
/* ------------------------------------------------------------ */

double smooth(float smooth_rate)
{
  int i,ix,iy;
  PTYPE h,dh,avg,sum1,sum2;
  PTYPE afac, n1, n2;
  PTYPE *hf2;
  PTYPE af1, af2;
  PTYPE dscale = 0;    /* average difference between neighboring pixels */
  int dcount;

  hf2 = (PTYPE *) malloc((size_t)sizeof(PTYPE)*(xsize+2)*(ysize+2));
   if (hf2 == NULL) {
     fprintf(stderr,"Could not allocate hf buffer memory.\n");
     exit(1);
   }

/* find dscale by sampling a few pixels in the grid */
    /*
   dcount = 0;
   dscale = 0;
   for (iy = 0; iy<ysize; iy+=5) {  
    for (ix = 0; ix<xsize; ix+=5) {
     h = Ehn(hf,ix,iy);
      sum1 = 0;
      af1 = 4;
      for (i=1;i<9;i+=2) {
       n1 = Ehn(hf,ix+xo[i+1],iy+yo[i+1]) - h; 
       if (n1 < 0) {  
	   n1 *= 2.0;
	   af1 += 1;     }
       sum1 += n1;
      } 
      dscale += sum1/af1;
      dcount++;                 
   }
  } 
  dscale /= dcount;       
  */
/* found dscale */


   for (iy = 0; iy<ysize; iy++) {  /* skip the edges */
    for (ix = 0; ix<xsize; ix++) {
     h = Ehn(hf,ix,iy);
      sum1 = 0;
      sum2 = 0;
      af1 = 4;
      af2 = 4;
      for (i=1;i<9;i+=2) {
       n1 = Ehn(hf,ix+xo[i+1],iy+yo[i+1]) - h; /* adj. neighbors */
       if (n1 < 0) {  /* count lower elements more */
	   n1 *= 2.0;
	   af1 += 1;     }
       n2 = Ehn(hf,ix+xo[i],iy+yo[i]) - h;     /* diagonal neighbors */  
       if (n2 < h) {
	   n2 *= 2.0;
	   af2 += 1;     }
       sum1 += n1;
       sum2 += n2;
      } /* end for i... */

      /* avg = 0.5858*(sum1/af1) + 0.4142*(sum2/af2); */
      avg = 1.0*(sum1/af1) + 0.0*(sum2/af2); /* weight closest neighbors heavily */
      dh = avg;   /* difference between this point and avg of neighbors */
      if (h > 1.0) h = 1;
      if (h < 0.0) h = 0;
		/* sign convention: positive afac = addition to elevation */
		/* what is the effect of nonlinear smoothing? hmmm. */

#define PCONST 0.6366197723     /* constant value 2/pi */

      afac = dh * PCONST*atan(0.1 * fabs(dh/smooth_rate));

				  /* altitude-dependent smoothing */
      if (pow_fac != 0)
	afac *= fabs(1 - (pow_offset + pow(h,pow_fac)));  

      if (h < er_thresh) {        /* only smooth terrain below altitude limit */
       Ehn(hf2,ix,iy) = h + afac;
       El(bn,ix,iy) += afac;            /* record addition */
       dscale = max(dscale,afac);
      } else {          /* if above threshold, NO erosion */
       Ehn(hf2,ix,iy) = h;
      }
    } /* end for ix */
   } /* end for iy */

   for (iy = 0; iy<ysize; iy++) {  /* copy entire matrix(minus edges) to hf*/
    for (ix = 0; ix<xsize; ix++) {
     Ehn(hf,ix,iy) = Ehn(hf2,ix,iy);
    } /* end for ix */
   } /* end for iy */

  free(hf2);
  return(dscale);        /* average difference in height */
} /* end smooth() */


/* ------------------------------------------------------------ *
 * slump  --  simulate land slippage; mudslides
 * ------------------------------------------------------------ */

void slump(double rate)
{
  int i,ix,iy;
  PTYPE h,dh,avg,sum1,sum2;
  PTYPE afac, n1, n2;
  PTYPE *hf2;
  PTYPE af1, af2;
  PTYPE dscale;    /* average difference between neighboring pixels */
  int dcount;

  hf2 = (PTYPE *) malloc((size_t)sizeof(PTYPE)*(xsize+2)*(ysize+2));
   if (hf2 == NULL) {
     fprintf(stderr,"Could not allocate hf buffer memory.\n");
     exit(1);
   }

/* find dscale by sampling a few pixels in the grid */

   dcount = 0;
   dscale = 0;
   for (iy = 0; iy<ysize; iy+=5) {  /* take samples of 1/25th of all */
    for (ix = 0; ix<xsize; ix+=5) {
     h = Ehn(hf,ix,iy);
      sum1 = 0;
      af1 = 4;
      for (i=1;i<9;i+=2) {
       n1 = Ehn(hf,ix+xo[i+1],iy+yo[i+1]) - h; /* adj. neighbors */
       if (n1 < 0) {  /* count lower elements more */
	   n1 *= 2.0;
	   af1 += 1;     }
       sum1 += n1;
      } /* end for i... */
      dscale += sum1/af1;
      dcount++;                 /* count number of summations */
   }
  } /* end for iy.. */
  dscale /= dcount;       /* really, dscale should be a fixed value */

/* found dscale */


   for (iy = 0; iy<ysize; iy++) {  /* skip the edges */
    for (ix = 0; ix<xsize; ix++) {
     h = Ehn(hf,ix,iy);
      sum1 = 0;
      sum2 = 0;
      af1 = 4;
      af2 = 4;
      for (i=1;i<9;i+=2) {
       n1 = Ehn(hf,ix+xo[i+1],iy+yo[i+1]) - h; /* adj. neighbors */
       if (n1 < 0) {  /* count lower elements more */
	   n1 *= 2.0;
	   af1 += 1;     }
       n2 = Ehn(hf,ix+xo[i],iy+yo[i]) - h;     /* diagonal neighbors */  
       if (n2 < h) {
	   n2 *= 2.0;
	   af2 += 1;     }
       sum1 += n1;
       sum2 += n2;
      } /* end for i... */

      /* avg = 0.5858*(sum1/af1) + 0.4142*(sum2/af2); */
      avg = 0.8*(sum1/af1) + 0.2*(sum2/af2); /* weight closest neighbors heavily */
      dh = avg;   /* difference between this point and avg of neighbors */
      if (h > 1.0) h = 1;
      if (h < 0.0) h = 0;
		/* sign convention: positive afac = addition to elevation */
		/* what is the effect of nonlinear smoothing? hmmm. */

#define PCONST 0.6366197723     /* constant value 2/pi */

      afac = dh * PCONST*atan( pow(rate*fabs(dh/dscale),3) ); /* slump! */

				  /* altitude-dependent slumping */
      if (pow_fac != 0)
	afac *= (1 - (pow_offset + pow(h,pow_fac)));  

      if (h < er_thresh) {        /* only smooth terrain below altitude limit */
       Ehn(hf2,ix,iy) = h + afac;
       El(bn,ix,iy) += afac;            /* record addition */
      } else {          /* if above threshold, NO erosion */
       Ehn(hf2,ix,iy) = h;
      }
    } /* end for ix */
   } /* end for iy */

   for (iy = 0; iy<ysize; iy++) {  /* copy entire matrix(minus edges) to hf*/
    for (ix = 0; ix<xsize; ix++) {
     Ehn(hf,ix,iy) = Ehn(hf2,ix,iy);
    } /* end for ix */
   } /* end for iy */

  free(hf2);
  /* return(dscale); */       /* average difference in height */
} /* end smooth() */


/* lowestn()    --      returns direction of steepest descent from */
/*                      element x,y in hf1[] array                 */

int lowestn(PTYPE *hf1, int x, int y, float *r)
{ int i, mini;
  float here, slope, min;

  mini = 0;  
  min = 1e30;
  here = Ehn(hf1,x,y);
  for (i=1;i<9;i++) {
    slope = Ehn(hf1,x+xo[i],y+yo[i]) - here;
    if ( (i==1) || (i==3) || (i==5) || (i==7) )
      slope = slope / 1.414;
    if (slope < min) {
      mini = i;
      min = slope;
    }
  }
  r[0] = min;
  return(mini);
}

/* ------------------------------------------------------------ */
/* find_upflow -- find direction of stream flow at each lattice */
/*                point, and set element of flag (uphill).      */
/*                Must be called after find_flow2() so fl set.  */
/* ------------------------------------------------------------ */

void find_upflow()
{
 int x, y,i;
 int xn, yn;
 int dir;
 int inflows;                    /* #nodes flowing in */
 register float mval;            /* current minimum elevation value */
 register float maxv;            /* current maximum */
 register float tval;            /* temporary value */

 for (y = 0; y < ysize; y++) {
   for (x = 0; x < xsize; x++)  {
	mval = Ehn(hf,x,y);          /* start with current point */
	fflush(stdout);
	maxv = mval;

	tval = Eh(hf,x,y);
	El(flag,x,y) = 0;       /* 0 means point is a peak */
	inflows = 0;            /* # neighbors pointing downhill to me */

	for (i=1;i<9;i++) {
	  tval = Ehn(hf,x+xo[i],y+yo[i]);       /* neighbor higher? */
	  if (tval > maxv) {
	    maxv = tval;                /* yes, set uphill->this one */
	    El(flag,x,y) = i;
	  }
	  xn = x + xo[i];
	  yn = y + yo[i];
			   /* make sure not off edge of mx */
	  if ((xn >= 0) && (xn < xsize) && (yn >= 0) && (yn < ysize)) {
	   dir = El(fl,xn,yn);       /* inflow from here? */
	   if ((xo[i]+xo[dir]==0) && (yo[i]+yo[dir]==0)) { /* inflow! */
	     inflows++;
	   }
	 }  /* end if (inbounds) */
	} /* end for i */
	
	if (inflows == 0) {     /* no inflows: find_ua can start here */
	     El(flag,x,y) = (El(flag,x,y) | 0x10);  /* set b4: summed */
	    /* printf("uflow %d,%d summed\n",x,y); */
	}

				/* and if we still think it's a peak...*/

	if (El(flag,x,y)==0) {       /* supposed to be peak */
	 for(i=1;i<9;i++) {
	   dir = El(fl,x+xo[i],y+yo[i]);
	   if ((xo[i]+xo[dir]==0) && (yo[i]+yo[dir]==0)) { /* inflow! */
	     El(flag,x,y)=1;  /* arbitrary direction */
	   }
	 } /* end for i */
	} /* end if d==0 */

   } /* end for x*/
 } /* end for  y */
} /* end find_upflow() routine */

/* ------------------------------------------------------------ */
/* find_flow2  --  find direction of stream flow at each lattice */
/*                point, and set element of fl matrix (downhill) */
/* ------------------------------------------------------------ */

void find_flow2()
{
 int x, y;                      /* matrix coord */
 float slope;

 for (y = 0; y < ysize; y++) {
   for (x = 0; x < xsize; x++)  {
	El(fl,x,y) = lowestn(hf,x,y,&slope);
   }  /* end for x */
 }  /* end for y */
}  /* end find_flow2() */

/* ------------------------------------------------------------ */
/*  quick_flow()  --  set fl[] array to: 0 if element is a      */
/*                    local minimum, 1 otherwise                */
/* ------------------------------------------------------------ */

void quick_flow(PTYPE *hf1)
{
 int x1, y1;
 register int x, y, i;
 register float mval;            /* current minimum elevation value */
 register float tval;            /* temporary value */

 for (y = 0; y < ysize; y++) {
    for (x = 0; x < xsize; x++)  {
	mval = Ehn(hf1,x,y);          /* start with current point */


	El(fl,x,y) = 0;                 /* start at 0 */
	for (i = 1;i<9;i++) {          /* check 4-neighbors */
	  x1 = x+xo[i];
	  y1 = y+yo[i];
	  tval = Ehn(hf1,x1,y1);
	  if (tval < mval) {
	    El(fl,x,y) = 1;
	    /* break;*/
	  } /* end if */
	} /* end for i */

   } /* end for ix */
 } /* end for iy */
} /* end quick_flow() */


/* ------------------------------------------------------------ */
int alloc_mem()                         /* alloc arrays */
{
size_t msize;

  msize = (size_t) ( xsize * ysize * sizeof(char));
  if ((fl = (BYTE *) malloc( msize ) ) == NULL)   {
   fprintf(stderr,"Could not allocate memory for direction array.\n");
   exit(1);
  }
  memset(fl,(char) 0, msize);

  msize = (size_t) ( xsize * ysize * sizeof(char));
  if ((flag = (BYTE *) malloc( msize ) ) == NULL)   {
   fprintf(stderr,"Could not allocate memory for flag array.\n");
   exit(1);
  }
  memset(flag,(char) 0, msize);

  msize = (size_t) ( xsize * ysize * sizeof(float));
  if ((ua = (float *) malloc( msize ) ) == NULL)   {
   fprintf(stderr,"Could not allocate memory for uphill-area array.\n");
   exit(1);
  }
  memset(ua,(char) 0, msize);

  if ((bn = (float *) malloc( msize ) ) == NULL)   {
   fprintf(stderr,"Could not allocate memory for basin-area array.\n");
   exit(1);
  }
  memset(bn,(char) 0, msize);

  return(0);
}

/* ------------------------------------------------------------ */
/*  fill_bn  --  fill in basins in heightfield area             */
/*               input is hf1, output is in hf2                 */
/* ------------------------------------------------------------ */

int fill_bn(PTYPE *hf1, PTYPE *hf2)
{
int ix,iy,i;
int changed;
float sum, hmax, neb, wavg;
float delta_h;         /* max elevation change this run */
float dhmax;           /* maximum elevation (?) */


  for (iy=0;iy<(ysize+2);iy++) {       /* start with exact copy */
   for (ix=0;ix<(xsize+2);ix++) {
    Eh(hf2,ix,iy) = Eh(hf1,ix,iy);
   }
  }

  changed = 0;                  /* no changes yet */
  dhmax = 0;
  for(iy=2;iy<(ysize);iy++)  {
    for (ix=2;ix<(xsize);ix++)  {
      if (El(fl,ix-1,iy-1) == 0)  {           /* local depression */
	sum = 0; hmax = -9E20;
	for (i=1;i<9;i++) {
	  neb = Eh(hf1,ix+xo[i],iy+yo[i]);      /* neighbor elev. */
	  sum += neb;                   /* get sum of neighbor elevs */
	  if (neb > hmax) hmax = neb;   /* get max elev. */
	}
	/* sum += hmax; */               /* add max a few more times */
	wavg = sum / 8;               /* simple average */
	delta_h = wavg - Eh(hf1,ix,iy);       /* diff between avg and this */
	if (delta_h > dhmax) dhmax = delta_h;
	Eh(hf2,ix,iy) = wavg;       /* hf2(x,y) <- weighted local avg */
	El(bn,ix-1,iy-1) += delta_h;    /* add to basin accumulation */
	/* printf("bn%1.2f ",El(bn,ix-1,iy-1)); */
	changed++;
      } /* end if */
      else {    /* if not depression, just copy old mx to new */
	Eh(hf2,ix,iy) = Eh(hf1,ix,iy);
      }
    } /* end for ix */
    /* printf("\n"); */
  } /* end for iy */

  printf("d%1.1e ",dhmax);
  return(changed);
}

double sgn(double a)
{
  if (a > 0)
   return (1);
  else if (a < 0)
   return (-1);
  else
   return (0);
}

double sgn1(double a)
{
 double sign = 0;

  if (a > 0.0) {
   sign = 1.0;
  } else if (a < 0) {
   sign = -1.0;
  }
  return(a);
}


/* rescale input matrix to lie between 0 and 1 */
void rescale()
{
 int ix, iy;
 float uval, umax, umin, scale;

 umax = -1e30;
 umin = 1e30;

   for (iy = 0; iy<(ysize+2); iy++) {
    for (ix = 0; ix<(xsize+2); ix++) {
	uval = Eh(hf,ix,iy);
      /*  printf("%f ",uval); */
	if (uval > umax) umax = uval;
	if (uval < umin) umin = uval;
    }
/*    printf("\n"); */
   }
   if ((umax-umin) == 0) {
     fprintf(stderr,"Fatal error: input matrix is constant.\n");
     exit(1);
   }
   scale = 1.0/(umax-umin);

   printf("Input max %1.3f  min %1.3f scale %1.3f\n",umax,umin,scale);
   for (iy = 0; iy<(ysize+2); iy++) {
    for (ix = 0; ix<(xsize+2); ix++) {
	Eh(hf,ix,iy) = scale * (Eh(hf,ix,iy)-umin);
    }
   }

} /* end rescale */

