/*
*$Id: texture.c,v 2.3 2006/02/09 03:09:01 glynn Exp $
************************************************************
* MODULE: r.le.pixel/texture.c                             *
*         Version 5.0                Nov. 1, 2001          *
*				                           *
* AUTHOR: W.L. Baker, University of Wyoming                *
*         BAKERWL@UWYO.EDU                                 *
*                                                          *
* PURPOSE: To analyze pixel-scale landscape properties     *
*         texture.c calculates the measures for r.le.pixel *
*				                           *
* COPYRIGHT: (C) 2001 by W.L. Baker                        *
*                                                          *
* This program is free software under the GNU General      *
* Public License(>=v2).  Read the file COPYING that comes  *
* with GRASS for details                                   *
*				                           *
************************************************************/

#include <grass/gis.h>
#include <grass/config.h>
#include "pixel.h"


					/* set the maximum number of categories
					   that can be in the input map */

#define NO_OF_CATEGORIES cats.num

					/* set external pointers and routines */

extern struct CHOICE *choice;
extern int g_scale, g_unit;

					/* define a structure used to read in
					   info about categories in the map */

struct Categories cats;
 
char *G_get_cat();

					/* declare a counter for the number
					   of pixels with non-null values
					   (total) in cal_edge and a flag for
					   pixels with null attribute in 
					   cal_divers */

int total, nullflag;




					/* MOVING WINDOW ANALYSIS DRIVER */

void mv_texture (int nrows, int ncols, double **buf, double **null_buf, double **value, int index, double *rich, int cnt, int cntwhole)

{
  int           lc;
  register int  i, j;
  double    	*atts, *edgeatts, attr[4], diver[4], edge[4], tex[5], **weight, 
               **edgemat;
  RASTER_MAP_TYPE data_type;

					/* set the contents of the arrays used
					   used to stored the results of the
					   diversity, texture, and edge 
					   calculations to zero */

  attr[0] = attr[1] = attr[2] = attr[3] = 0;
  diver[0] = diver[1] = diver[2] = diver[3] = 0;
  tex[0] = tex[1] = tex[2] = tex[3] = tex[4] = 0;
  edge[0] = edge[1] = edge[2] = edge[3] = 0;


					/*************************/
					/* The weight matrix in  */
					/* "r.le.para/weight"    */
					/* must have the follow- */
					/* ing format: a, b, c   */
					/*    = category values  */
					/*                       */
					/*      a     b   c    	 */
					/* a	0.0 0.1 0.1      */
					/* b 	0.1 0.1 0.1      */
					/* c 	0.2 0.2 0.3 	 */
					/*************************/

					/* If juxtaposition is to be 
					   calculated, then dynamically 
					   allocate memory for the atts
					   array and the weight matrix */

  if (choice->jux[0]){
     atts = (double *)G_calloc(cntwhole, sizeof(double));
     weight = (double **)G_calloc(cntwhole, sizeof(double *));
     for(i = 0; i < cntwhole; i++)
        weight[i] = (double *)G_calloc(cntwhole, sizeof(double));

					/* read in the weight matrix */
      
     read_weight(cntwhole, atts, weight);
     total = 0;
  }


					/*************************/
					/* The edge matrix in    */
					/* "r.le.para/edge"      */
					/* must have the follow- */
					/* ing format: a, b, c   */
					/*    = category values  */
					/*                       */
					/*      a b c            */
					/* a	0 1 1	         */
					/* b 	1 0 1            */
					/* c 	1 1 0 	         */
					/*************************/

					/* If edge by type is to be
					   calculated, then dynamically 
					   allocate memory for the edgeatts
					   array and the edgemat matrix */


  if (choice->edg[2]){
     edgeatts = (double *)G_malloc(cntwhole*sizeof(double));
     edgemat = (double **)G_malloc(cntwhole*sizeof(double *));
     for(i = 0; i < cntwhole; i++)
        edgemat[i] = (double *)G_malloc(cntwhole*sizeof(double));

					/* read in the edge matrix */
      
     read_edge(cntwhole, edgeatts, edgemat);
  }

  					/* main calculation loop.  Do the
					   calculations for each pixel in
					   the sampling area */

  for(i = 1; i < nrows + 1; i++) {
     for(j = 1; j < ncols + 1; j++) {

                                        /* find the sequence number of the
					   attribute in the richness array */

        if ((buf[i][j] || buf[i][j] == 0.0) && null_buf[i][j] == 0.0)
           lc = check_order(buf[i][j], rich);


					/* based on the choices made, call
					   the appropriate calc. routine */
 
	if (choice->att[0])
	   cal_att(buf, null_buf, i, j, nrows, ncols, attr);

        if (choice->te2[0])
           cal_tex(buf, null_buf, i, j, nrows, ncols, lc, rich, cnt, tex);
 
        if (choice->edg[0] || choice->jux[0]) 
           cal_edge(buf, null_buf, i, j, nrows, ncols, lc, edge, cntwhole,
		    atts, weight, edgeatts, edgemat, 0, 0);

      	if (choice->div[0])
           cal_divers(buf, null_buf, i, j, nrows, ncols, lc, cnt, diver);

     }
  }

  if (choice->jux[0]) {
     G_free (atts);
     for (i = 0; i < cntwhole; i++)
        G_free (weight[i]);
     G_free (weight);
  }

  if (choice->edg[2]) {
     G_free (edgeatts);
     for (i = 0; i < cntwhole; i++)
	G_free (edgemat[i]);
     G_free (edgemat);
  }
  					/* put the calculated value for the
					   selected measure into the 
					   corresponding pixel in the output
					   map */

  if (choice->att[0]) {				/* ATTRIBUTE MEASURES */
     if (choice->att[1])
        value[index][0] = attr[0];                      /* Mean */
     if (choice->att[2])
        value[index][1] = attr[1];                      /* St. dev. */
     if (choice->att[3])
        value[index][2] = attr[2];                      /* Min. */
     if (choice->att[4])
        value[index][3] = attr[3];                      /* Max. */
  }

  if (choice->div[0]) {				/* DIVERSITY MEASURES */
     if (choice->div[1])
	value[index][4] = diver[0];                     /* Richness */
     if (choice->div[2])
        value[index][5] = diver[1];                     /* Shannon */
     if (choice->div[3]) 
        value[index][6] = diver[2];                     /* Dominance */
     if (choice->div[4]) 
        value[index][7] = diver[3];                     /* Inv. Simpson */
  }

  if (choice->te2) {   				/* TEXTURE MEASURES */
     if (choice->te2[1])                                /* Contagion */
        value[index][8] = tex[0];
     if (choice->te2[2])                                /* ASM */
        value[index][9] = tex[1];
     if (choice->te2[3])                                /* IDM */
        value[index][10] = tex[2];
     if (choice->te2[4])                                /* Entropy */
        value[index][11] = tex[3];
     if (choice->te2[5])                                /* Contrast */
        value[index][12] = tex[4];  
  }
 
  if (choice->jux[0]) {				/* JUXTA. MEASURES */
     if (choice->jux[1])
        value[index][13] = edge[0];                    /* Mean jux. */
     if (choice->jux[2])	
        value[index][14] = edge[2];                   /* St.dev. jux. */
  } 

  if (choice->edg[0]) {				/* EDGE MEASURES */
     if (choice->edg[1])
        value[index][15] = edge[1];                   /* Sum of edges */
     if (choice->edg[2])
	value[index][16] = edge[3];                   /* Sum of edges 
                                                          by type */
  }

  return;
}




					/* WHOLE MAP, UNITS, OR REGIONS 
					   DRIVER */

void df_texture (int nrows, int ncols, double **buf, double **null_buf, double *rich, int cnt, int cntwhole)

{
  FILE   	*fp0, *fp1, *fp2, *fp3, *fp4, *fp5;											
  int           lc, fd, *edge1, *edge2, fc, **edgenull;
  register int  i, j;
  double    	*atts, *edgeatts, attr[4], diver[4], edge[4], tex[5], **weight, 
                **edgemat;
  CELL          **edgemap_c, *edge_buf_c;
  FCELL         **edgemap_f, *edge_buf_f;
  DCELL		**edgemap_d, *edge_buf_d, *zscor_buf;
  RASTER_MAP_TYPE data_type;

  data_type = G_raster_map_type(choice->fn, G_mapset());  

					/* set the contents of the arrays
					   used to stored the results of the
					   diversity, texture, and edge 
					   calculations to zero */

  attr[0] = attr[1] = attr[2] = attr[3] = 0;
  diver[0] = diver[1] = diver[2] = diver[3] = 0;
  edge[0] = edge[1] = edge[2] = edge[3] = 0;
  tex[0] = tex[1] = tex[2] = tex[3] = tex[4] = 0;

					/*************************/
					/* The weight matrix in  */
					/* "r.le.para/weight"    */
					/* must have the follow- */
					/* ing format: a, b, c   */
					/*    = category values  */
					/*                       */
					/*      a     b   c    	 */
					/* a	0.0 0.1 0.1      */
					/* b 	0.1 0.1 0.1      */
					/* c 	0.2 0.2 0.3 	 */
					/*************************/

					/* If juxtaposition is to be 
					   calculated, then dynamically 
					   allocate memory for the atts
					   array and the weight matrix */

  if (choice->jux[0]) {
     atts = (double *)G_calloc(cntwhole, sizeof(double));
     weight = (double **)G_calloc(cntwhole, sizeof(double *));
     for (i = 0; i < cntwhole; i++)
        weight[i] = (double *)G_calloc(cntwhole, sizeof(double));

					/* read in the weight matrix */
      
     read_weight(cntwhole, atts, weight);
     total = 0;
  }


					/*************************/
					/* The edge matrix in    */
					/* "r.le.para/edge"      */
					/* must have the follow- */
					/* ing format: a, b, c   */
					/*    = category values  */
					/*                       */
					/*      a b c            */
					/* a	0 1 1	         */
					/* b 	1 0 1            */
					/* c 	1 1 0 	         */
					/*************************/

					/* If edge by type is to be 
					   calculated, then dynamically 
					   allocate memory for the edgeatts
					   array and the edgemat matrix */


  if (choice->edg[2]) {
     edgeatts = (double *)G_calloc(cntwhole, sizeof(double));
     edgemat = (double **)G_calloc(cntwhole, sizeof(double *));
     for (i = 0; i < cntwhole; i++) {
        edgemat[i] = (double *)G_calloc(cntwhole, sizeof(double));
     }

					/* read in the edge matrix */
      
     read_edge(cntwhole, edgeatts, edgemat);
  }


                      			/* dynamically allocate storage for the
                                   	   buffer that will hold the map of the
                                   	   edges and for the edgenull array */

  if (choice->edgemap) {
     switch (data_type) {
        case (CELL_TYPE):
           edgemap_c = (CELL **)G_calloc(nrows + 3, sizeof(CELL *));
           for(i = 0; i < nrows + 3; i++)
              edgemap_c[i] = (CELL *)G_calloc(ncols + 3, sizeof(CELL));
           break;
        case (FCELL_TYPE):
           edgemap_f = (FCELL **)G_calloc(nrows + 3, sizeof(FCELL *));
           for(i = 0; i < nrows + 3; i++)
              edgemap_f[i] = (FCELL *)G_calloc(ncols + 3, sizeof(FCELL));
           break;
        case (DCELL_TYPE):
           edgemap_d = (DCELL **)G_calloc(nrows + 3, sizeof(DCELL *));
           for(i = 0; i < nrows + 3; i++)
              edgemap_d[i] = (DCELL *)G_calloc(ncols + 3, sizeof(DCELL));
           break;
     }
     edgenull = (int **)G_calloc(nrows + 3, sizeof(int *));
     for (i = 0; i < nrows + 3; i++) {
        edgenull[i] = (int *)G_calloc(ncols + 3, sizeof(int));
     }
     for (i = 1; i < nrows + 1; i++)
        for (j = 1; j < ncols + 1; j++)
           *(*(edgenull + i) + j) = 1;
  }

  					/* main calculation loop.  Do the
					   calculations for each pixel in
					   the map */

   for (i = 1; i < nrows + 1; i++) {
     for (j = 1; j < ncols + 1; j++) {

                                        /* find the sequence number of the
					   attribute in the richness array */
   
        if ((buf[i][j] || buf[i][j] == 0.0) && null_buf[i][j] == 0.0)
           lc = check_order(buf[i][j], rich);

					/* based on the choices made, call
					   the appropriate calc. routine */

        if (choice->att[0])
	   cal_att(buf, null_buf, i, j, nrows, ncols, attr); 

        if (choice->div[0]) 
           cal_divers(buf, null_buf, i, j, nrows, ncols, lc, cnt, diver);

        if (choice->jux[0] || choice->edg[0]) {
           edge1 = edge2 = 0;
           cal_edge(buf, null_buf, i, j, nrows, ncols, lc, edge, cntwhole,
                    atts, weight, edgeatts, edgemat, &edge1, &edge2);

	   if (choice->edgemap) {
	      if (edge1) {
                 switch (data_type) {
		    case (CELL_TYPE):
                       *(*(edgemap_c + i) + j) = *(*(buf + i) + j);
		       *(*(edgemap_c + i + 1) + j) = *(*(buf + i + 1) + j);
		       break;
		    case (FCELL_TYPE):
                       *(*(edgemap_f + i) + j) = *(*(buf + i) + j);
		       *(*(edgemap_f + i + 1) + j) = *(*(buf + i + 1) + j);
		       break;
		    case (DCELL_TYPE):
                       *(*(edgemap_d + i) + j) = *(*(buf + i) + j);
		       *(*(edgemap_d + i + 1) + j) = *(*(buf + i + 1) + j);
		       break;
                 }
                 *(*(edgenull  + i) + j) = 0;
                 *(*(edgenull  + i + 1) + j) = 0;
              }
              if (edge2) {
                 switch (data_type) {
		    case (CELL_TYPE):
                       *(*(edgemap_c + i) + j) = *(*(buf + i) + j);
                       *(*(edgemap_c + i) + j + 1) = *(*(buf + i) + j + 1);
		       break;
		    case (FCELL_TYPE):
                       *(*(edgemap_f + i) + j) = *(*(buf + i) + j);
                       *(*(edgemap_f + i) + j + 1) = *(*(buf + i) + j + 1);
		       break;
		    case (DCELL_TYPE):
                       *(*(edgemap_d + i) + j) = *(*(buf + i) + j);
                       *(*(edgemap_d + i) + j + 1) = *(*(buf + i) + j + 1);
		       break;
                 }
                 *(*(edgenull  + i) + j) = 0;
                 *(*(edgenull  + i) + j + 1) = 0;
	      }
	   }
	}

        if (choice->te2[0])
           cal_tex(buf, null_buf, i, j, nrows, ncols, lc, rich, cnt, tex);
     }
  }

  if (choice->jux[0]) {
     G_free (atts);
     for(i = 0; i < cntwhole; i++)
        G_free (weight[i]);
     G_free (weight);
  }

  if (choice->edg[2]) {
     G_free (edgeatts);
     for (i = 0; i < cntwhole; i++)
        G_free (edgemat[i]);
     G_free (edgemat);
  }   

					/* if the edge map was requested */

  if (choice->edgemap) {
     fc = G_open_raster_new("edge", data_type);
     switch (data_type) {
        case (CELL_TYPE):
           edge_buf_c = G_allocate_raster_buf(CELL_TYPE);
           for (i = 1; i < nrows + 1; i++) {
	      G_zero_raster_buf(edge_buf_c, CELL_TYPE);
              G_set_null_value(edge_buf_c, ncols + 1, CELL_TYPE);
              for (j = 1; j < ncols + 1; j++) {
                 if (*(*(edgenull + i) + j) == 0)
	            *(edge_buf_c + j - 1) = edgemap_c[i][j];
              }
              G_put_raster_row(fc, edge_buf_c, CELL_TYPE);
           }
	   break;
        case (FCELL_TYPE):
           edge_buf_f = G_allocate_raster_buf(FCELL_TYPE);
           for (i = 1; i < nrows + 1; i++) {
	      G_zero_raster_buf(edge_buf_f, FCELL_TYPE);
              G_set_null_value(edge_buf_f, ncols + 1, FCELL_TYPE);
              for (j = 1; j < ncols + 1; j++) {
                 if (*(*(edgenull + i) + j) == 0)
	            *(edge_buf_f + j - 1) = edgemap_f[i][j];
              }
              G_put_raster_row(fc, edge_buf_f, FCELL_TYPE);
           }
	   break;
        case (DCELL_TYPE):
           edge_buf_d = G_allocate_raster_buf(DCELL_TYPE);
           for (i = 1; i < nrows + 1; i++) {
	      G_zero_raster_buf(edge_buf_d, DCELL_TYPE);
              G_set_null_value(edge_buf_d, ncols + 1, DCELL_TYPE);
              for (j = 1; j < ncols + 1; j++) {
                 if (*(*(edgenull + i) + j) == 0)
	            *(edge_buf_d + j - 1) = edgemap_d[i][j];
              }
              G_put_raster_row(fc, edge_buf_d, DCELL_TYPE);
           }
	   break;
     }
     switch (data_type) {
        case (CELL_TYPE):
           G_free (edge_buf_c);
           for(i = 0; i < nrows + 3; i++)
              G_free (edgemap_c[i]);
           G_free (edgemap_c);
	   break;
        case (FCELL_TYPE):
           G_free (edge_buf_f);
           for(i = 0; i < nrows + 3; i++)
              G_free (edgemap_f[i]);
           G_free (edgemap_f);
	   break;
        case (DCELL_TYPE):
           G_free (edge_buf_d);
           for(i = 0; i < nrows + 3; i++)
              G_free (edgemap_d[i]);
           G_free (edgemap_d);
	   break;
     }
     for (i = 0; i < nrows + 3; i++)
        G_free (edgenull[i]);
     G_free (edgenull);
     G_close_cell(fc);
  }

					/* if the zscore map was requested */

  if (choice->z) {
     fd = G_open_raster_new("zscores", DCELL_TYPE);
     zscor_buf = G_allocate_raster_buf(DCELL_TYPE);
     for (i = 1; i < nrows + 1; i++) {
        G_zero_raster_buf(zscor_buf, DCELL_TYPE);
        G_set_null_value(zscor_buf, ncols + 1, DCELL_TYPE);
        for(j = 1; j < ncols + 1; j++) {
           if (attr[1] > 0.0)
	      if ((buf[i][j] || buf[i][j] == 0) && null_buf[i][j] == 0.0)
                 *(zscor_buf + j - 1) = (buf[i][j] - attr[0])/attr[1];
        }
        G_put_raster_row(fd, zscor_buf, DCELL_TYPE);
     }
     G_free (zscor_buf);
     G_close_cell(fd);
  }

					/* open the output files and 
					   save the calculated values */

  if (choice->att[0]) {
     fp0 = fopen0("r.le.out/b1-4.out", "a");
     fprintf(fp0, "%5d%5d", g_scale, g_unit);
     fprintf(fp0,"     %10.3f  %10.3f  %10.3f  %10.3f\n",
        attr[0], attr[1], attr[2], attr[3]);
     fclose(fp0);
  }

  if (choice->div[0]) {
     fp1 = fopen0("r.le.out/d1-4.out", "a"); 
     fprintf(fp1, "%5d%5d", g_scale, g_unit);
     fprintf(fp1,"     %10.3f  %10.3f  %10.3f  %10.3f\n",
        diver[0], diver[1], diver[2], diver[3]);
     fclose(fp1);
  }

  if (choice->te2[0]) {
     fp2 = fopen0("r.le.out/t1-5.out", "a");
     fprintf(fp2, "%5d%5d", g_scale, g_unit);
     fprintf(fp2,"     %10.3f  %10.3f  %10.3f  %10.3f  %10.3f\n",
        tex[0], tex[1], tex[2], tex[3], tex[4]);
     fclose(fp2);
  } 

  if (choice->jux[0]) {
     fp3 = fopen0("r.le.out/j1-2.out", "a");
     fprintf(fp3, "%5d%5d", g_scale, g_unit);
     fprintf(fp3,"     %10.3f  %10.3f\n", edge[0], edge[2] );
     fclose(fp3);
  }

  if (choice->edg[1]) {
     fp4 = fopen0("r.le.out/e1.out", "a");
     fprintf(fp4, "%5d%5d", g_scale, g_unit);
     fprintf(fp4,"     %10.0f\n", edge[1]);
     fclose(fp4);
  }

  if (choice->edg[2]) {
     fp5 = fopen0("r.le.out/e2.out", "a");
     fprintf(fp5, "%5d%5d", g_scale, g_unit);
     fprintf(fp5,"     %10.0f\n", edge[3]);
     fclose(fp5);	
  }

  return;
}





					/* ATTRIBUTE CALC. */

void cal_att (double **buf, double **null_buf, int i0, int j0, int nr, int nc, double *attr)

{
          
  static int    count;
  static double mini, maxi;
  double    	mean, stdv;
  static double sum, sum2;

  if (i0==1 && j0 == 1) {
     sum   = 0.0;
     sum2  = 0.0;
     count = 0;
     maxi  = 0.0;
     mini  = BIG;
     mean  = 0.0;
     stdv  = 0.0;
  }

  if ((buf[i0][j0] || buf[i0][j0] == 0.0) && null_buf[i0][j0] == 0.0) {
     count++;
     sum += buf[i0][j0];
     sum2 += buf[i0][j0] * buf[i0][j0];

     if (buf[i0][j0] > maxi) 
        maxi = buf[i0][j0];

     if (buf[i0][j0] < mini)
        mini = buf[i0][j0];
  }
 
  if (i0 == nr && j0 == nc) {

					/* calc. b1 = mean pixel attr. */

     attr[0] = mean = sum/count;

					/* calc. b2 = st. dev. pixel attr. */

     stdv = sum2/count -  mean*mean;
     if (stdv > 0)
        attr[1] = sqrt(stdv);

					/* calc. b3 = min. pixel attr. */

     attr[2] = mini;

					/* calc. b4 = max. pixel attr. */

     attr[3] = maxi;
  }

  return;
}




					/* DIVERSITY CALC. */

void cal_divers (double **buf, double **null_buf, int i0, int j0, int nr, int nc, int lc, int cnt, double *diver)

{
  int           tot; 
  static int    *density;
  register int  i, k=0;
  double    	p, entr;

					/* if this is the first pixel, 
					   dynamically allocate memory for
					   the density array */

  if (1 == i0 && 1 == j0)   
     density = (int *)G_calloc(cnt, sizeof(int));


					/* if the pixel has a non-null 
					   attribute */

  if ((buf[i0][j0] || buf[i0][j0] == 0) && null_buf[i0][j0] == 0.0) {

					/* increment the density count for
					   the right element of the density
					   array */

     density[lc] ++;

  }					
  
					/* if this is the last pixel in the
					   sampling area, do the calc. */

  if (i0 == nr && j0 == nc) {

					/* initialize the counter for the
					   total number of pixels */

     tot = 0;
     diver[0] = cnt;            	/* richness */
     if (cnt > 1)
        entr = log((double)(cnt));
     else
	entr = 0.0;

					/* calculate the total # of pixels */

     for (i = 0; i < cnt; i++) {
/*printf("i=%d tot=%d density[%d]=%d\n",i,tot,i,density[i]);  */
	tot = tot + density[i];
     }

     for (i = 0; i < cnt; i++) {
        if(density[i] > 0 && tot > 0) {
           p = density[i] / (double)(tot);
           diver[1] += -(p * log(p));  	/* Shannon */
/*printf("i=%d p=%f shann=%f\n", i, p, diver[1]);  */
           diver[3] += p * p;
	}
     }
     diver[2] = entr - diver[1];     	/* dominance */

     diver[3] = 1/diver[3];          	/* recip. Simpson */
     G_free (density);

  }

  return;
}







					/* TEXTURE CALC. */

void cal_tex (double **buf, double **null_buf, int i0, int j0, int nr, int nc, int lc, double *rich, int cnt, double *tex)

{

  int           r, ln;
  double	p;
  static int  **GLCM;
  register int  i, j;
  int           GLCM_sum = 0 ;


                                        /* setup the GLCM matrix */

  if ( (i0 == 1 && j0 == 1) ){
     GLCM = (int **)G_calloc(cnt, sizeof(int *));
     for(i = 0; i < cnt; i++)
        GLCM[i] = (int *)G_calloc(cnt, sizeof(int));
  }


  					/* calculate the GLCM, using
					   the appropriate one of the
					   seven texture methods
					   (parameter te1) */

  if ((buf[i0][j0] || buf[i0][j0] == 0.0) && null_buf[i0][j0] == 0.0) {
     if (i0 > 1) {
        if (choice->tex == 3 ||
            choice->tex == 5 ||
            choice->tex == 7) {
            if ((buf[i0-1][j0] ||
                 buf[i0-1][j0] == 0.0) &&
                 null_buf[i0-1][j0] == 0.0) {
               ln = check_order(buf[i0-1][j0], rich);
               GLCM[lc][ln] ++;
            }
        }

        if (j0 > 1 && (choice->tex == 4 ||
                       choice->tex == 6 ||
                       choice->tex == 7)) {
           if ((buf[i0-1][j0-1] ||
                buf[i0-1][j0-1] == 0.0) &&
                null_buf[i0-1][j0-1] == 0.0) {
              ln = check_order(buf[i0-1][j0-1], rich);
              GLCM[lc][ln] ++;
           }
        }

        if (j0 < nc &&(choice->tex == 2 ||
                       choice->tex == 6 ||
                       choice->tex == 7)){
           if ((buf[i0-1][j0+1] ||
                buf[i0-1][j0+1] == 0.0) &&
                null_buf[i0-1][j0+1] == 0.0) {
              ln = check_order(buf[i0-1][j0+1], rich);
              GLCM[lc][ln] ++;
           }
        }
     }

     if (i0 < nr) {
        if (choice->tex == 3 ||
            choice->tex == 5 ||
            choice->tex == 7){
           if ((buf[i0+1][j0] ||
                buf[i0+1][j0] == 0.0) &&
                null_buf[i0+1][j0] == 0.0) {
              ln = check_order(buf[i0+1][j0], rich);
              GLCM[lc][ln] ++;
           }
        }

        if (j0 > 1 && (choice->tex == 2 ||
                       choice->tex == 6 ||
		       choice->tex == 7)) {
           if ((buf[i0+1][j0-1] ||
                buf[i0+1][j0-1] == 0.0) &&
                null_buf[i0+1][j0-1] == 0.0) {
              ln = check_order(buf[i0+1][j0-1], rich);
              GLCM[lc][ln] ++;
           }
        }

        if (j0 < nc && (choice->tex == 4 ||
                        choice->tex == 6 ||
	   	     choice->tex == 7)) {
           if ((buf[i0+1][j0+1] ||
                buf[i0+1][j0+1] == 0.0) &&
                null_buf[i0+1][j0+1] == 0.0) {
              ln = check_order(buf[i0+1][j0+1], rich);
              GLCM[lc][ln] ++;
           }
        }
     }

     if (j0 > 1 && (choice->tex == 1 ||
                    choice->tex == 5 ||
	            choice->tex == 7)) {
        if ((buf[i0][j0-1] ||
             buf[i0][j0-1] == 0.0) &&
             null_buf[i0][j0-1] == 0.0) {
           ln = check_order(buf[i0][j0-1], rich);
           GLCM[lc][ln]++;
        }
     }

     if (j0 < nc && (choice->tex == 1 ||
                     choice->tex == 5 ||
	             choice->tex == 7)) {
        if ((buf[i0][j0+1] ||
             buf[i0][j0+1] == 0.0) &&
             null_buf[i0][j0+1] == 0.0) {
           ln = check_order(buf[i0][j0+1], rich);
           GLCM[lc][ln]++;
        }
     }
  }


 					/* once the end of the buffer
					   has been reached, sum up the
                                           contents of the GLCM */


  if (i0 == nr && j0 == nc) {    

     for(i = 0; i < cnt; i++) {
/*printf("%3d %3d %3d %3d %3d\n",GLCM[i][0],GLCM[i][1],GLCM[i][2],
                                 GLCM[i][3],GLCM[i][4]);  */
        for(j = 0; j < cnt; j++)
           GLCM_sum = GLCM_sum + GLCM[i][j];
     }

     r = GLCM_sum ;

    				        /* for each category, compute the
					   Pij and the measures */

     for(i = 0; i < cnt; i++)
        for(j = 0; j < cnt; j++)
           if (p = GLCM[i][j] / (double)(r)) {
	      tex[3] += p*log(p);
	      tex[1] += p*p;                  /* ASM */
	      tex[2] += p/(1+ (rich[i]-rich[j])*(rich[i]-rich[j]));   /* IDM */
	      tex[4] += p*(rich[i]-rich[j])*(rich[i]-rich[j]);        /* Contrast */
           }


     if (tex[3])
        tex[3] = -1.0 * tex[3];	  	      /* Entropy */
     tex[0] = 2*log((double)(cnt)) - tex[3];  /* Contagion */

     for(i = 0; i < cnt; i++)
        G_free (GLCM[i]);
     G_free (GLCM);
  }

  return;
}



					/* EDGE, JUXTAPOSITION CALC. */

void cal_edge (double **buf, double **null_buf, int i0, int j0, int nr, int nc, int lc, double *edge, int cntwhole, double *atts, double **weight, double *edgeatts, double **edgemat, int *edge1, int *edge2)

{

  int		 ln, lr, cnt, fr, to;
  double 	 juxta, sum, stdv;
  static double  sum2 = 0;  


/* VARIABLES:
	atts = 		an array of the attributes; read from weight file.
	weight =	weight from the weight file.
	edgeatts =	
	edgemat = 	
	cntwhole =	richness (total number of attributes)
	ln =		sequence number, for the neighbor pixel, in the atts 
			   array
	lc =		sequence number for an attribute in the richness 
			   array (NO LONGER USED)
	lr =		sequence number, for the current pixel, in the atts
			   array
	edge1 =		sum of edges requested
	edge2 =		sum of edges by type requested 
	edge[0] =	running total of juxtaposition values
	edge[1] =	sum of edges
	edge[2] = 	st. dev. juxtaposition
	edge[3] = 	sum of edges by type
*/

					/* initialize variables */

  sum = cnt = 0;

					/* juxtaposition calc. step 1: 
					   each 4 line loop checks first
					   that the pixel has non-null
					   attribute, second finds the
					   sequence number for the 
					   pixel's attribute in the 
					   attribute array, third sums
					   the quantity and weight, and
					   fourth sums the correct cnt */

					/* if this pixel has a non-null
					   attribute, do the calculations */

  if ((buf[i0][j0] || 
       buf[i0][j0] == 0) &&
       null_buf[i0][j0] == 0.0) {                          /*1*/

					/* increment the count of non-zero
					   pixels */


     total ++;
     if (choice->jux[0])
        lr = find_loc(cntwhole, atts, buf[i0][j0]);

					/* if this pixel is not on the
					   edge of the map in the first
					   row, and if juxt. was chosen */
				   
     if (i0 > 1 && choice->jux[0]) {


					/* neighbor 1: i0-1,j0 */

        if ((buf[i0-1][j0] || buf[i0-1][j0] == 0.0) && null_buf[i0-1][j0] == 0.0) {
           ln = find_loc(cntwhole, atts, buf[i0-1][j0]);
           sum += 2*weight[lr][ln];
           cnt += 2;
        }

					/* if this pixel is not on the
					   edge of the map in the first
					   col */

        if (j0 > 1){

					/* neighbor 2: i0-1,j0-1 */

           if ((buf[i0-1][j0-1] || buf[i0-1][j0-1] == 0.0) && null_buf[i0-1][j0-1] == 0.0) {
	      ln = find_loc(cntwhole, atts, buf[i0-1][j0-1]);
              sum += weight[lr][ln];
              cnt ++;
           }
        }

					/* if this pixel is not on the
					   edge of the map in the last
					   col */

        if (j0 < nc) {

					/* neighbor 3: i0-1,j0+1 */

           if ((buf[i0-1][j0+1] || buf[i0-1][j0+1] == 0.0) && null_buf[i0-1][j0+1] == 0.0) {
	      ln = find_loc(cntwhole, atts, buf[i0-1][j0+1]);
              sum += weight[lr][ln];
              cnt ++;
           }
        }
     }

					/* if this pixel is not on the
					   edge of the map in the last
					   row and if juxta. was chosen */

     if (i0 < nr) {
        if (choice->jux[0]) {

					/* neighbor 4: i0+1,j0 */

           if ((buf[i0+1][j0] || buf[i0+1][j0] == 0.0) && null_buf[i0+1][j0] == 0.0) {
              ln = find_loc(cntwhole, atts, buf[i0+1][j0]);
              sum += 2*weight[lr][ln];
              cnt += 2;
           }

					/* if this pixel is not on the
					   edge of the map in the first
					   col */

           if (j0 > 1) {

					/* neighbor 5: i0+1,j0-1 */

              if ((buf[i0+1][j0-1] || buf[i0+1][j0-1] == 0.0) && null_buf[i0+1][j0-1] == 0.0) {
                 ln = find_loc(cntwhole, atts, buf[i0+1][j0-1]);
                 sum += weight[lr][ln];
                 cnt ++;
              }
           }

					/* if this pixel is not on the
					   edge of the map in the last
					   col */

           if (j0 < nc){

					/* neighbor 6: i0+1,j0+1 */

              if ((buf[i0+1][j0+1] || buf[i0+1][j0+1] == 0.0) && null_buf[i0+1][j0+1] == 0.0) {
	         ln = find_loc(cntwhole, atts, buf[i0+1][j0+1]);
                 sum += weight[lr][ln];
	         cnt ++;
              } 
           }
        }
					/* if the i0+1, j0 pixel is different,
					   and both pixels have non-null
					   attributes */

        if (choice->edg[0] && (buf[i0][j0] != buf[i0+1][j0]) && (buf[i0+1][j0] ||
            buf[i0+1][j0] == 0.0) && null_buf[i0+1][j0] == 0.0) {

 					/* then increment edge[1] to 
					   indicate that an edge has been 
					   found if sum of edges requested */

           if (choice->edg[1])
              edge[1]++;

					/* then increment edge[2] to 
					   indicate that an edge has been
					   found if the edge is one of the
					   types requested and if sum of 
					   edges by type requested */

           if (choice->edg[2]) {
              fr = find_edge(cntwhole, edgeatts, buf[i0][j0]);
              to = find_edge(cntwhole, edgeatts, buf[i0+1][j0]);
              if (edgemat[fr][to]) {
                 edge[3]++;
                 if (choice->edgemap)
                    *edge1 = 1;
              }
           }
        }                                  
     }


					/* regardless which row this pixel
					   is in (even if edge row); if 
					   this pixel is not on the edge
					   of the map in the first col, and
					   if juxta. was chosen */

     if (j0 > 1 && choice->jux[0]) {

					/* neighbor 7: i0,j0-1 */

        if ((buf[i0][j0-1] || buf[i0][j0-1] == 0.0) && null_buf[i0][j0-1] == 0.0) {
           ln = find_loc(cntwhole, atts, buf[i0][j0-1]);
           sum += 2*weight[lr][ln];
           cnt += 2;
        }
     }

					/* regardless which row this pixel
					   is in (even if edge row); if 
					   this pixel is not on the edge
					   of the map in the last col, and
					   if juxta. was chosen */


     if (j0 < nc) {
        if (choice->jux[0]) {

					/* neighbor 8: i0,j0+1 */

           if ((buf[i0][j0+1] || buf[i0][j0+1] == 0.0) && null_buf[i0][j0+1] == 0.0)   {
              ln = find_loc(cntwhole, atts, buf[i0][j0+1]);
              sum += 2*weight[lr][ln]; 
                 cnt += 2;
           }
        }


 					/* if the i0, j0+1 pixel is different,
					   and both pixels have non-zero
					   attributes */

        if (choice->edg[0] && (buf[i0][j0] != buf[i0][j0+1]) && (buf[i0][j0+1] ||
            buf[i0][j0+1] == 0.0) && null_buf[i0][j0+1] == 0.0) {

 					/* then increment edge[1] to 
					   indicate that an edge has been 
					   found if sum of edges requested */

           if (choice->edg[1])
              edge[1]++;

					/* then increment edge[2] to 
					   indicate that an edge has been
					   found if the edge is one of the
					   types requested and if sum of 
					   edges by type requested */

           if (choice->edg[2]) {
              fr = find_edge(cntwhole, edgeatts, buf[i0][j0]);
              to = find_edge(cntwhole, edgeatts, buf[i0][j0+1]);
              if (edgemat[fr][to]) {
                 edge[3]++;
                 if (choice->edgemap)
                      *edge2 = 1;
              }
           }
        }
     }
  }

					/* calculate juxtaposition and 
					   add it to the running total
					   in edge[0] */

  if (choice->jux[0]) {
     if (cnt)
        juxta = sum / cnt;
     else
        juxta = 0.0;
     edge[0] += juxta;
     sum2 += juxta*juxta;
  }


					/* if this is the last pixel in 
					   the sampling area and juxta-
					   position was chosen */

  if (choice->jux[0] && i0 == nr && j0 == nc) {

     edge[0] /= total;

     stdv = (double)sum2/total - edge[0]*edge[0];
     if (stdv > 0) 
        edge[2] = sqrt(stdv);
      
     sum2 = 0;
  }

  return;
}




					/* READ THE WEIGHT FILE */

void read_weight (int richcount, double atts[], double **weight, int *attcnt)

{
  FILE         *fp;
  register int i, j;
  float        tmp;

					/* open the weight file */

  fp = fopen2("r.le.para/weight", "r");

					/* read the attributes into 
					   the atts array */

  for(i = 0; i < richcount; i++) {
     fscanf(fp, "%f", &tmp);
     atts[i] = tmp;
  }
  while(fgetc(fp) != '\n')
     if (fgetc(fp) != ' ') {
        fprintf(stdout, "\n");
        fprintf(stdout, "   *************************************************\n");
        fprintf(stdout, "    The weight file (r.le.para/weight) is incorrect \n");
        fprintf(stdout, "       since more/less than the %d attributes found \n",richcount);
        fprintf(stdout, "       in the map are listed in the weight file     \n");
        fprintf(stdout, "   *************************************************\n");
        exit(1);
     }
					/* read the weights, skipping
					   the first column, which
					   contains the attribute again */

  for(i = 0; i < richcount; i++){
     fscanf(fp, "%f", &tmp);
     for(j = 0; j < i; j++){
        weight[i][j] = weight[j][i];
     }
     for(j = 0; j < richcount; j++){
        fscanf(fp, "%f", &tmp);
        weight[i][j] = tmp;
     }
     while(fgetc(fp) != '\n');
  }
  fclose(fp);

  return;
}






					/* READ THE EDGE FILE */

void read_edge (int richcount, double atts[], double **edge)

{
  FILE         *fp;
  register int i, j;
  float        tmp;

					/* open the edge file */

  fp = fopen3("r.le.para/edge", "r");

					/* read the attributes into
					   the atts array */

  for(i = 0; i < richcount; i++) {
     fscanf(fp, "%f", &tmp);
     atts[i] = tmp;
  }
  while(fgetc(fp) != '\n')
     if (fgetc(fp) != ' ') {
        fprintf(stdout, "\n");
        fprintf(stdout, "   *************************************************\n");
        fprintf(stdout, "    The edge file (r.le.para/edge) is incorrect     \n");
        fprintf(stdout, "       since more/less than the %d attributes found \n",richcount);
        fprintf(stdout, "       in the map are listed in the edge file       \n");
        fprintf(stdout, "   *************************************************\n");
        exit(1);
     }

					/* read the edge weights, skipping
					   the first column, which 
					   contains the attribute again */

  for(i = 0; i < richcount; i++){
     fscanf(fp, "%f", &tmp);
     for(j = 0; j < i; j++){
        edge[i][j] = edge[j][i];
     }
     for(j = 0; j < richcount; j++){
        fscanf(fp, "%f", &tmp);
        edge[i][j] = tmp;
     }
     while(fgetc(fp) != '\n');
  }
  fclose(fp);

  return;
}






					/* FIND THE SEQUENCE NUMBER FOR
					   AN ATTRIBUTE IN THE ATTRIBUTE
					   ARRAY WHICH IS READ FROM THE
					   WEIGHT FILE */

int find_loc (int richcount, double atts[], double test)

{
  register int i;
  
  G_sleep_on_error(0);
  for(i = 0; i < richcount; i++) {
     if (test == atts[i]) return i;
  }
  G_fatal_error("The weight file in r.le.para is incorrect, exit\n");

  return(0);
}



					/* FIND THE SEQUENCE NUMBER FOR
					   AN ATTRIBUTE IN THE ATTRIBUTE
					   ARRAY WHICH IS READ FROM THE
					   EDGE FILE */

int find_edge (int richcount, double atts[], double test)

{
  register int i;
  
  G_sleep_on_error(0);
  for(i = 0; i < richcount; i++) {
     if (test == atts[i]) {
        return i;
     }
  }
  G_fatal_error("The edge file in r.le.para is incorrect, exit\n");

  return(0);
}




					/* FIND THE SEQUENCE NO. OF AN
					   ATTRIBUTE IN THE RICHNESS ARRAY */

int check_order (double att, double *rich)

{
  int i = 0;

  while(att != rich[i]) i++;
  return i;
  
}





