/*
   File:        mapshape.c
   Author:      Andrew W. Moore
   Created:     Mon Mar 22 05:44:03 EST 1999
   Description: Density Map

   Copyright 1999, The Auton Lab
*/

#include "mapshape.h"
#include "distutils.h"
#include "ongr.h"

mapshape *mk_default_mapshape(int size)
{
  mapshape *ms = AM_MALLOC(mapshape);
  ms -> data_bound = mk_hrect_from_strings("0 0","1 1");
  ms -> ag_bound = mk_hrect_from_strings("0 0","512 512");
  ms -> agrows = size;
  ms -> agcols = size;
  return ms;
}

void fprintf_mapshape(FILE *s,char *m1,mapshape *x,char *m2)
{
  char *prefix;

  prefix = mk_printf("%s -> data_bound",m1);
  fprintf_hrect(s,prefix,x->data_bound,m2);
  free_string(prefix);

  prefix = mk_printf("%s -> ag_bound",m1);
  fprintf_hrect(s,prefix,x->ag_bound,m2);
  free_string(prefix);

  prefix = mk_printf("%s -> agrows",m1);
  fprintf_int(s,prefix,x->agrows,m2);
  free_string(prefix);

  prefix = mk_printf("%s -> agcols",m1);
  fprintf_int(s,prefix,x->agcols,m2);
  free_string(prefix);
}

void pmapshape(mapshape *x)
{
  fprintf_mapshape(stdout,"ms",x,"\n");
}

/* Given that the data is distributed so that hr is its bounding box,
   this function makes a reasonable distance metric for it in which
   the i'th component of the distance metric is simply the width of
   the i'th component of a box. 

   MAKES and returns the metric. Eventually someone will have to free
   it with free_dyv(metric) */
dyv *mk_default_metric_for_hrect(hrect *hr)
{
  dyv *x = mk_dyv(hrect_size(hr));
  int i;
  for ( i = 0 ; i < hrect_size(hr) ; i++ )
    dyv_set(x,i,real_max(1e-10,hrect_width_ref(hr,i)));
  return x;
}


/* What's a distance metric?

   In this code we assume that the squared distance(point1,point2) =

     M-1
     ____
     \       point1[i] - point2[i]
      \    ( --------------------- )^2
      /            metric[i]
     /___
     i=0
*/
/* Given that the data is distributed so that hr is its bounding box,
   this function makes a reasonable distance metric for it.

   all_equal_metric TRUE means metric[i] = 1.0
   all_equal_metric FALSE means metric[i] = width_of_ith_dimension

   MAKES and returns the metric. Eventually someone will have to free
   it with free_dyv(metric) */
dyv *mk_metric_for_hrect(hrect *hr,bool all_equal_metric)
{
  dyv *metric;
  if ( all_equal_metric )
    metric = mk_constant_dyv(hrect_size(hr),1.0);
  else
    metric = mk_default_metric_for_hrect(hr);
  return metric;
}

/* PRE: Two dimensional */
hrect *mk_ag_bounds_for_metric_and_bounding_box(dyv *metric,hrect *hr)
{
  extern double Frame_x_lo;
  extern double Frame_y_lo;
  extern double Frame_x_hi;
  extern double Frame_y_hi;
  extern int Window_width;
  extern int Window_height;

  double dw = hrect_width_ref(hr,0);
  double dh = hrect_width_ref(hr,1);

  double agw = Frame_x_hi - Frame_x_lo;
  double agh = Frame_y_hi - Frame_y_lo;

  double fw = (double) Window_width;
  double fh = (double) Window_height;

  /* pixel_x = fw/agw * (ag_x - ag_x_lo)
     ag_x    = ag_x_lo + bw/dw * (data_x - data_x_lo) */
  /* pixel_y = fh/agh * (ag_y - ag_y_lo)
     ag_y    = ag_y_lo + bh/dh * (data_y - data_y_lo) */

  /* if data_b_x = data_a_x + metric_x
        data_b_y = data_a_y + 0

	data_c_x = data_a_x + 0
        data_c_y = data_a_y + metric_y

      then I want (pixel_b_x - pixel_a_x) = (pixel_c_y - pixel_a_y)

     And that means I need bw = z * bh where z is defined below.

     I also want the box size to be as large as will fit, which means
     I want max(bw/agw,bh/agh) = 1

     Thus I get bh = min(agw/z,agh) */

  double metric_x = dyv_ref(metric,0);
  double metric_y = dyv_ref(metric,1);

  double z = (metric_y/metric_x) * (dw/dh) * (fh/fw) * (agw/agh);

  double bh = real_min(agw/z,agh);
  double bw = z * bh;

  double ag_mid_x = (Frame_x_lo + Frame_x_hi)/2.0;
  double ag_mid_y = (Frame_y_lo + Frame_y_hi)/2.0;

  double b_mid_x = ag_mid_x;
  double b_mid_y = ag_mid_y;

  double b_lo_x = b_mid_x - bw/2.0;
  double b_hi_x = b_mid_x + bw/2.0;
  double b_lo_y = b_mid_y - bh/2.0;
  double b_hi_y = b_mid_y + bh/2.0;

  dyv *lo = mk_dyv_2(b_lo_x,b_lo_y);
  dyv *hi = mk_dyv_2(b_hi_x,b_hi_y);

  hrect *ag_bounds = mk_hrect(lo,hi);

  free_dyv(lo);
  free_dyv(hi);
  return ag_bounds;
}

mapshape *mk_mapshape_for_drawing(dyv *metric,hrect *bounds)
{
  extern int Window_width;
  extern int Window_height;
  extern double Frame_x_lo;
  extern double Frame_y_lo;
  extern double Frame_x_hi;
  extern double Frame_y_hi;

  extern double Frame_x_lo;
  extern double Frame_y_lo;
  extern double Frame_x_hi;
  extern double Frame_y_hi;
  extern int Window_width;
  extern int Window_height;

  double agw = Frame_x_hi - Frame_x_lo;
  double agh = Frame_y_hi - Frame_y_lo;

  double fw = (double) Window_width;
  double fh = (double) Window_height;

  double bw;
  double bh;

  mapshape *ms = AM_MALLOC(mapshape);

  ms -> data_bound = mk_copy_hrect(bounds);
  ms -> ag_bound = mk_ag_bounds_for_metric_and_bounding_box(metric,bounds);

  bw = hrect_width_ref(ms->ag_bound,0);
  bh = hrect_width_ref(ms->ag_bound,1);

  ms -> agrows = (int) floor(0.5 + fh * bh / agh);
  ms -> agcols = (int) floor(0.5 + fw * bw / agw);

  return ms;
}

mapshape *mk_mapshape_from_hrect(hrect *bounds)
{
  dyv *metric = mk_dyv_2(hrect_width_ref(bounds,0),
			 hrect_width_ref(bounds,1));
  mapshape *ms;

  /*
    set_ag_frame(hrect_lo_ref(bounds,0),hrect_lo_ref(bounds,1),
    hrect_hi_ref(bounds,0),hrect_hi_ref(bounds,1));
  */

  
  ms = mk_mapshape_for_drawing(metric,bounds);
  free_dyv(metric);
  return ms;
}

mapshape *mk_mapshape_for_comparing(dyv *metric,hrect *bounds)
{
  mapshape *ms = mk_mapshape_for_drawing(metric,bounds);
  int rows_times_cols = 512 * 512;
  double agcols,agrows;
  double u;

  /* I want new_agrows/new_agcols == old_agrows/old_agcols =def= u
     I want new_agrows * new_agcols == rows_times_cols

     new_agrows = u * new_agcols
     new_agcols^2 * u = rows_times_cols
     new_agcols = sqrt(rows_times_cols/u)
  */

  u = ms->agrows / (double)ms->agcols;
  agcols = sqrt(rows_times_cols / u);
  agrows = u * agcols;

  ms ->agrows = (int)floor(0.5 + agrows);
  ms ->agcols = (int)floor(0.5 + agcols);

  return ms;
}

void free_mapshape(mapshape *ms)
{
  free_hrect(ms->ag_bound);
  free_hrect(ms->data_bound);
  AM_FREE(ms,mapshape);
}

mapshape *mk_copy_mapshape(mapshape *ms)
{
  mapshape *newms = AM_MALLOC(mapshape);
  newms -> data_bound = mk_copy_hrect(ms->data_bound);
  newms -> ag_bound = mk_copy_hrect(ms->ag_bound);
  newms -> agrows = ms -> agrows;
  newms -> agcols = ms -> agcols;
  return newms;
}


bool mapshape_matches_dmap(mapshape *ms,dym *dmap)
{
  return ms->agrows == dym_rows(dmap) && ms->agcols == dym_cols(dmap);
}

void dym_add_constant(dym *dmap,double delta)
{
  int i,j;
  for ( i = 0 ; i < dym_rows(dmap) ; i++ )
    for ( j = 0 ; j < dym_cols(dmap) ; j++ )
      dym_increment(dmap,i,j,delta);
}

void normalize_dmap(dym *dmap)
{
  double dmin = dym_min(dmap);

  if ( dmin < 0.0 )
  {
    if ( dmin < 1e-06 )
      printf("***\n*** WARNING: DMap has -ve components.\n"
	     "*** My first step will be to add a constant amount\n"
	     "*** (%g) to all entries to repair this problem.\n"
	     "***\n",-dmin);
    dym_add_constant(dmap,-dmin);
  }
  dym_scalar_mult(dmap,1.0/dym_sum(dmap),dmap);
}

void hrect_rowcol_to_coords(hrect *hr,int rows,int cols,int row,int col,
			    double *r_x,double *r_y)
{
  double zcol = col / (double) cols;
  double zrow = row / (double) rows;
  *r_x = hrect_lo_ref(hr,0) + zcol * hrect_width_ref(hr,0);
  *r_y = hrect_lo_ref(hr,1) + zrow * hrect_width_ref(hr,1);
}

void mapshape_rowcol_to_datapoint(mapshape *ms,int row,int col,
                                  double *r_x,double *r_y)
{
  hrect_rowcol_to_coords(ms->data_bound,ms->agrows,ms->agcols,row,col,
			 r_x,r_y);
}

void mapshape_rowcol_to_agcoords(mapshape *ms,int row,int col,
				 double *r_x,double *r_y)
{
  hrect_rowcol_to_coords(ms->ag_bound,ms->agrows,ms->agcols,row,col,
			 r_x,r_y);
}

void hrect_coords_to_rowcol(hrect *hr,int rows,int cols,
			    double x,double y,int *r_row,int *r_col)
{
  double zcol = (x - hrect_lo_ref(hr,0))/hrect_width_ref(hr,0);
  double zrow = (y - hrect_lo_ref(hr,1))/hrect_width_ref(hr,1);
  *r_row = (int) floor(rows * zrow);
  *r_col = (int) floor(cols * zcol);
}

void mapshape_agcoord_to_rowcol(mapshape *ms,double x,double y,
				int *r_row,int *r_col)
{
  hrect_coords_to_rowcol(ms->ag_bound,ms->agrows,ms->agcols,x,y,r_row,r_col);
}

void mapshape_datapoint_to_rowcol(mapshape *ms,double x,double y,
				int *r_row,int *r_col)
{
  hrect_coords_to_rowcol(ms->data_bound,ms->agrows,ms->agcols,x,y,r_row,r_col);
}

void phrect(hrect *hr)
{
  fprintf_hrect(stdout,"hr",hr,"\n");
}

double hrect_one_coord_transform(hrect *a,hrect *b,int dim,double ax)
{
  double z = (ax - hrect_lo_ref(a,dim))/hrect_width_ref(a,dim);
  double bx = hrect_lo_ref(b,dim) + z * hrect_width_ref(b,dim);
  return bx;
}

void hrect_coord_transform(hrect *a,hrect *b,double ax,double ay,
			   double *r_bx,double *r_by)
{
  *r_bx = hrect_one_coord_transform(a,b,0,ax);
  *r_by = hrect_one_coord_transform(a,b,1,ay);
}

void mapshape_datapoint_to_agcoords(mapshape *ms,double x,double y,
				    double *r_agx,double *r_agy)
{
  hrect_coord_transform(ms->data_bound,ms->ag_bound,x,y,r_agx,r_agy);
}

void mapshape_agcoords_to_datapoint(mapshape *ms,double agx,double agy,
				    double *r_x,double *r_y)
{
  hrect_coord_transform(ms->ag_bound,ms->data_bound,agx,agy,r_x,r_y);
}

dyv *mk_datapoint_from_rowcol(mapshape *ms,int row,int col)
{
  double x,y;
  mapshape_rowcol_to_datapoint(ms,row,col,&x,&y);
  return mk_dyv_2(x,y);
}

dym *mk_dmap_dym(mapshape *ms)
{
  return mk_zero_dym(ms->agrows,ms->agcols);
}

imat *mk_agmap_from_dmap_and_thresh(dym *dmap,double thresh)
{
  int rows = dym_rows(dmap);
  int cols = dym_cols(dmap);
  int i,j;
  imat *agmap = mk_imat(rows,cols);

  for ( i = 0 ; i < rows ; i++ )
    for ( j = 0 ; j < cols ; j++ )
    {  
      bool on = dym_ref(dmap,i,j) > thresh;
      imat_set(agmap,i,j,(on)?AG_RED:AG_WHITE);
    }

  return agmap;
}

dyv *mk_dmap_dyv_from_all_dym_rows(dym *x)
{
  int rows = dym_rows(x);
  int cols = dym_cols(x);
  dyv *v = mk_dyv(rows * cols);
  int i,j;
  for ( i = 0 ; i < rows ; i++ )
    for ( j = 0 ; j < cols ; j++ )
      dyv_set(v,i*cols+j,dym_ref(x,i,j));
  return v;
}

double dmap_frac_to_thresh(dym *dmap,double frac)
{
  dyv *values = mk_dmap_dyv_from_all_dym_rows(dmap);
  int k = int_min(dyv_size(values)-1,
                     int_max(0,(int)(dyv_size(values) * frac)));
  double thresh;

  printf("Finding thresh for frac %g... ",frac);
  fflush(stdout);
  thresh = dyv_kth_smallest(values,k);
  free_dyv(values);
  printf("...finished\n");
  return thresh;
}

imat *mk_agmap_from_dmap_and_frac(dym *dmap,double frac)
{
  double thresh = dmap_frac_to_thresh(dmap,frac);
  return mk_agmap_from_dmap_and_thresh(dmap,thresh);
}


void render_agmap_line(mapshape *ms,int i,int j1,int j2)
{
  double x1,y1;
  double x2,y2;

  mapshape_rowcol_to_agcoords(ms,i,j1,&x1,&y1);
  mapshape_rowcol_to_agcoords(ms,i,j2,&x2,&y2);
  ag_line(x1,y2,x2,y2);
}

void render_agmap(mapshape *ms,imat *agmap)
{
  int i;
  int prev_nonwhite_col = -1;

  set_ag_frame(0.0,0.0,512.0,512.0);

  printf("Begin rendering agmap...");
  fflush(stdout);
  for ( i = 0 ; i < ms->agrows ; i++ )
  {
    int prevcol = imat_ref(agmap,i,0);
    int prevj = 0;
    int j;
    for ( j = 1 ; j < ms->agcols ; j++ )
    {
      int col = imat_ref(agmap,i,j);
      if ( col != prevcol || j == imat_cols(agmap)-1 )
      {
        int nextj = (col==prevcol) ? imat_cols(agmap) : j;
        if ( prevcol != AG_WHITE )
        {
          if ( prev_nonwhite_col != prevcol )
          {
            ag_set_pen_color(prevcol);
            prev_nonwhite_col = prevcol;
          }
          render_agmap_line(ms,i,prevj,nextj);
        }
        prevj = j;
        prevcol = col;
      }
    }
  }
  printf("..finished\n");
}

void slow_render_agmap(mapshape *ms,imat *agmap)
{
  int i;

  set_ag_frame(0.0,0.0,(double)ms->agcols,(double)ms->agrows);
  ag_set_pen_color(AG_GREEN);
  ag_box(0.0,0.0,512.0,512.0);

  printf("Begin rendering agmap...");
  fflush(stdout);
  for ( i = 0 ; i < ms->agrows ; i++ )
  {
    int j;
    for ( j = 0 ; j < ms->agcols ; j++ )
    {
      int col = imat_ref(agmap,i,j);
      ag_set_pen_color(col);
      ag_pixel((double)j,(double)i);
    }
  }
  printf("..finished\n");
}

void render_dmap(mapshape *ms,dym *dmap,int dmap_type,double frac);

void regular_render_dmap(mapshape *ms,dym *dmap,double frac)
{
  imat *agmap = mk_agmap_from_dmap_and_frac(dmap,frac);
  render_agmap(ms,agmap);
  free_imat(agmap);
}

void animate_dmap(mapshape *ms,dym *dmap)
{
  dyv *dv = mk_dmap_dyv_from_all_dym_rows(dmap);
  ivec *indexes = mk_indices_of_sorted_dyv(dv);
  int k;
  ag_set_pen_color(AG_PURPLE);
  for ( k = 0 ; k < ivec_size(indexes) ; k++ )
  {
    int ival = ivec_ref(indexes,ivec_size(indexes)-k-1);
    int i = ival / ms->agcols;
    int j = ival % ms->agcols;
    double x,y;
    mapshape_rowcol_to_datapoint(ms,i,j,&x,&y);

    if ( dym_ref(dmap,i,j)>0.0 ) ag_pixel(x,y);
    if ( k % 100 == 0 ) am_usleep(500);
  }
  free_ivec(indexes);
  free_dyv(dv);
}

dym *mk_dmap_from_filename(char *fname)
{
  char *errmess;
  dym *dmap = mk_dym_from_filename(fname,&errmess);
  if ( dmap == NULL )
  {
    printf("%s\n",errmess);
    free_string(errmess);
  }
  else
    normalize_dmap(dmap);

  return dmap;
}

void save_dmap(char *fname,dym *dmap)
{
  FILE *s = safe_fopen(fname,"w");
  save_dym(s,dmap);
  fclose(s);
}

dyv *mk_cumulative_dyv(dyv *v)
{
  int size = dyv_size(v);
  dyv *c = mk_dyv(size);
  int i;
  double sum = 0.0;
  for ( i = 0 ; i < size ; i++ )
  {
    sum += dyv_ref(v,i);
    dyv_set(c,i,sum);
  }
  dyv_scalar_mult(c,1.0/dyv_ref(c,size-1),c);
  return c;
}

/* PRE: dyv is sorted in ascending order.
   POST: dyv_ref(dv,result) <= x and dyv_ref(dv,result+1) >= x if x <
         dyv_max(dv).
         if ( x >= dyv_max(dv) then result = dyv_size(dv)-1.
*/
int sorted_dyv_search(dyv *dv,double x)
{
  int lo = 0;
  int hi = dyv_size(dv)-1;
  int result;
  if ( x <= dyv_ref(dv,lo) )
    result = lo;
  else if ( x >= dyv_ref(dv,hi) )
    result = hi;
  else
  {
    while ( hi - lo > 1 )
    {
      int mid = (lo + hi) / 2;
      if ( dyv_ref(dv,mid) < x )
        lo = mid;
      else
        hi = mid;
    }
    result = lo;
    if ( dyv_ref(dv,result) > x || dyv_ref(dv,result+1) < x )
      my_error("Binary chop failed");
  }
  return result;
}
    
int index_from_cumulative(dyv *c)
{
  double r = range_random(0.0,1.0);
  int index = sorted_dyv_search(c,r);
  return index;
}

dym *mk_generate_x_from_dmap(mapshape *ms,dym *dmap,int num_points)
{
  int cols = dym_cols(dmap);
  int k;
  dyv *dv = mk_dmap_dyv_from_all_dym_rows(dmap);
  dyv *cum = mk_cumulative_dyv(dv);
  dym *data = mk_dym(num_points,2);

  ag_set_pen_color(AG_BLACK);

  for ( k = 0 ; k < num_points ; k++ )
  {
    int index = index_from_cumulative(cum);
    int i = index / cols;
    int j = index % cols;
    double x,y;
    double agx,agy;
    mapshape_rowcol_to_datapoint(ms,i,j,&x,&y);
    mapshape_rowcol_to_agcoords(ms,i,j,&agx,&agy);

    ag_pixel(agx,agy);

    dym_set(data,k,0,x);
    dym_set(data,k,1,y);
  }

  free_dyv(dv);
  free_dyv(cum);
  return data;
}

void save_dmap_sample(mapshape *ms,char *fname,dym *dmap,int num_points)
{
  dym *data = mk_generate_x_from_dmap(ms,dmap,num_points);
  save_dym_to_filename(fname,data);
  free_dym(data);
  printf("Saved %d randomly generated points to %s\n",num_points,fname);
}


void smooth_dmap(dym *dmap)
{
  dym *d2 = mk_copy_dym(dmap);
  int i,j;
  for ( i = 0 ; i < dym_rows(dmap) ; i++ )
    for ( j = 0 ; j < dym_cols(dmap) ; j++ )
    {
      int count = 0;
      double sum = 0.0;
      int di,dj;
      int maxd = 2;

      for ( di = i-maxd ; di <= i+maxd ; di++ )
        for ( dj = j-maxd ; dj <= j+maxd ; dj++ )
	{
	  if ( di >= 0 && di < dym_rows(dmap) &&
	       dj >= 0 && dj < dym_rows(dmap) )
	  {
	    count += 1;
	    sum += dym_ref(d2,di,dj);
	  }
	}

      dym_set(dmap,i,j,sum/count);
    }
  normalize_dmap(dmap);
  free_dym(d2);
}

dyv *mk_dmap_dyv_from_mouse(mapshape *ms)
{
  double agx,agy;
  int row,col;
  (void)ag_get_xy(&agx,&agy);
  mapshape_agcoord_to_rowcol(ms,agx,agy,&row,&col);

  return mk_datapoint_from_rowcol(ms,row,col);
}


void ag_dmap_dyv_dot(mapshape *ms,dyv *x)
{
  double agx,agy;
  mapshape_datapoint_to_agcoords(ms,dyv_ref(x,0),dyv_ref(x,1),&agx,&agy);
  ag_dot(agx,agy);
}

void ag_dmap_dyv_line(mapshape *ms,dyv *x1,dyv *x2)
{
  double agx1,agy1;
  double agx2,agy2;
  mapshape_datapoint_to_agcoords(ms,dyv_ref(x1,0),dyv_ref(x1,1),&agx1,&agy1);
  mapshape_datapoint_to_agcoords(ms,dyv_ref(x2,0),dyv_ref(x2,1),&agx2,&agy2);
  ag_line(agx1,agy1,agx2,agy2);
}

dym *mk_dmap_from_data(mapshape *ms,dym *data)
{
  dym *dmap = mk_dmap_dym(ms);
  int k;
  for ( k = 0 ; k < dym_rows(data) ; k++ )
  {
    double x = dym_ref(data,k,0);
    double y = dym_ref(data,k,1);
    int i,j;
    mapshape_datapoint_to_rowcol(ms,x,y,&i,&j);

    if ( i >= 0 && i < ms->agrows && j >= 0 && j < ms->agcols )
      dym_increment(dmap,i,j,1.0);
  }
  normalize_dmap(dmap);
  return dmap;
}

dym *mk_dmap_from_smoothed_data(mapshape *ms,dym *data,int num_smooths)
{
  dym *dmap = mk_dmap_from_data(ms,data);
  int k;
  for ( k = 0 ; k < num_smooths ; k++ )
  {
    printf("Smooth iteration %d of %d\n",k,num_smooths);
    smooth_dmap(dmap);
  }
  return dmap;
}

void score_dmap(dym *dmap,dym *true_dmap,
		double *r_sum_abs_prob_diff,
		double *r_sum_sqd_prob_diff,
		double *r_kl_divergence,
		double *r_sum_true_entropy)
{
  int i,j;
  double sum_abs_prob_diff = 0.0;
  double sum_sqd_prob_diff = 0.0;
  double kl_divergence = 0.0;
  double sum_true_entropy = 0.0;

  for ( i = 0 ; i < dym_rows(dmap) ; i++ )
    for ( j = 0 ; j < dym_cols(dmap) ; j++ )
    {  
      double true_prob = dym_ref(true_dmap,i,j);
      double prob = dym_ref(dmap,i,j);
      sum_abs_prob_diff += fabs(true_prob - prob);
      sum_sqd_prob_diff += real_square(true_prob - prob);
      kl_divergence += 
	prob * ( log(real_max(1e-26,prob)) - log(real_max(1e-50,true_prob)) );

      sum_true_entropy += true_prob * log(real_max(1e-50,true_prob));

    }
  *r_sum_abs_prob_diff = sum_abs_prob_diff;
  *r_sum_sqd_prob_diff = sum_sqd_prob_diff;
  kl_divergence /= log(2.0);
  *r_kl_divergence = kl_divergence;
  sum_true_entropy /= log(2.0);
  *r_sum_true_entropy = sum_true_entropy;
}

double dmap_kl_score(dym *dmap,dym *true_dmap)
{
  double sad,ssd,kl,ste;
  score_dmap(dmap,true_dmap,&sad,&ssd,&kl,&ste);
  return kl;
}

void report_score_dmap(dym *dmap,dym *true_dmap)
{
  double sad,ssd,kl,ste;
  score_dmap(dmap,true_dmap,&sad,&ssd,&kl,&ste);
  printf("Sum Abs Prob Diff = %g\n",sad);
  printf("Sum Sqd Prob Diff = %g\n",ssd);
  printf("Sum True Entropy = %g\n",ste);
  printf("***** KL Divergence = %g\n",kl);
}

void rsd_help(dym *dmap,dym *true_dmap,char *message)
{
  printf("%s\n",message);
  report_score_dmap(dmap,true_dmap);
  printf("\n");
}

int best_grid_dmap_num_smooths(mapshape *ms,dym *true_dmap,dym *x)
{
  int i;
  double best_smooth_score = -77.7;
  int best_smooth = -1;
  int max_smooth = 100;

  dym *smoothed_dmap = mk_dmap_from_smoothed_data(ms,x,0);

  for ( i = 0 ; i < max_smooth ; i++ )
  {
    double sad,ssd,kl,ste;
    score_dmap(smoothed_dmap,true_dmap,&sad,&ssd,&kl,&ste);
    printf("Smooth %3d: sad %9.6f, kl %9.6f\n",i,sad,kl);
    if ( best_smooth < 0 || best_smooth_score > kl )
    {
      best_smooth = i;
      best_smooth_score = kl;
    }
    smooth_dmap(smoothed_dmap);
  }

  free_dym(smoothed_dmap);
  return best_smooth;
}

void compare_score_dmap(mapshape *ms,dym *dmap,dym *true_dmap,dym *x)
{
  int best_smooth = best_grid_dmap_num_smooths(ms,true_dmap,x);
  dym *smoothed_dmap = mk_dmap_from_smoothed_data(ms,x,best_smooth);

  rsd_help(true_dmap,true_dmap,"True DMAP....");
  rsd_help(dmap,true_dmap,"Centers DMAP....");
  rsd_help(smoothed_dmap,true_dmap,"BestSmooth DMAP....");

  printf("Best Smooth Level = %d\n",best_smooth);

  free_dym(smoothed_dmap);
}

hrect *mk_hrect_defined_by_dmap_indexes(mapshape *ms,
					int ilo,int jlo,int ihi,int jhi)
{
  dyv *lo = mk_datapoint_from_rowcol(ms,ilo,jlo);
  dyv *hi = mk_datapoint_from_rowcol(ms,ihi,jhi);
  hrect *hr = mk_hrect(lo,hi);
  free_dyv(lo);
  free_dyv(hi);
  return hr;
}

/********** DMAP TYPES ***********/

int num_dmap_types(void)
{
  return NUM_DMAP_TYPES;
}

char *dmap_type_to_name(int dmap_type)
{
  char *result = NULL;
  switch ( dmap_type )
  {
    case REGULAR_DMAP: result = "regular"; break;
    case ANIMATE_DMAP: result = "animate"; break;
    case COLOR_DMAP: result = "color"; break;
    case GRAYSCALE_DMAP: result = "grayscale"; break;
    case GOOD_DMAP: result = "good"; break;
    case PEAK_DMAP: result = "peak"; break;
    case RIDGE_DMAP: result = "ridge"; break;
    default: my_error("Unknown dmap type");
  }
  return result;
}

/* Returns -1 if no such dmap type */
int name_to_dmap_type(char *name)
{
  int result = -1;
  int i;
  for ( i = 0 ; result < 0 && i < num_dmap_types() ; i++ )
    if ( eq_string(name,dmap_type_to_name(i)) )
      result = i;
  return result;
}

int dmap_type_from_args(char *key,int argc,char *argv[],int default_dmap_type)
{
  char *name = string_from_args(key,argc,argv,NULL);
  int dmap_type = (name==NULL) ? -1 : name_to_dmap_type(name);
  return ( dmap_type < 0 ) ? default_dmap_type : dmap_type;
}

int dmap_type_from_command(command *c,char *key,int default_dmap_type)
{
  int dmap_type = -1;

  while ( dmap_type < 0 )
  {
    char *name;
    int i;
    printf("Legal dmap types: ");
    for ( i = 0 ; i < num_dmap_types() ; i++ )
      printf("%s ",dmap_type_to_name(i));
    printf("\n");

    name = mk_string_from_command(c,key,dmap_type_to_name(default_dmap_type));
    dmap_type = name_to_dmap_type(name);
    if ( dmap_type < 0 )
      printf("%s is not a legal dmap type\n",name);
    free_string(name);
  }

  return dmap_type;
}

dyv *mk_sorted_dmap_dyv_from_all_dym_rows(dym *dmap)
{
  dyv *x = mk_dmap_dyv_from_all_dym_rows(dmap);
  dyv_sort(x,x);
  return x;
}

/* 
   result := dyv describing where the contours between color 
            bands should lie. This is of size num_bands+1.

  *r_colors := of size num_bands. colors[0] is the color
               for heights between result[0]-result[1], etc.

               All heights are greater than result[0] and 
               less than result[num_bands] */
dyv *old_mk_sensible_dmap_threshes(dym *dmap,ivec **r_colors)
{
  double dlo = dym_min(dmap);
  double dhi = dym_max(dmap);
  int i = 1;
  dyv *ticks;
  int ts;
  double lo,hi,delta;

  sensible_limits(dlo,dhi,&lo,&hi,&delta);

  ticks = mk_dyv_1(lo);

  while ( dyv_ref(ticks,dyv_size(ticks)-1) <= dhi )
  {
    add_to_dyv(ticks,lo+i*delta);
    i+= 1;
  }

  *r_colors = mk_ivec_1(AG_WHITE);
  
  ts = dyv_size(ticks);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_CYAN);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_GREEN);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_YELLOW);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_RED);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_MAGENTA);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_PURPLE);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_BLUE);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_GRAY);
  if ( ts-1 > ivec_size(*r_colors) ) add_to_ivec(*r_colors,AG_BLACK);
  if ( ts-1 > ivec_size(*r_colors) ) my_error("Too many ticks");

  return ticks;
}

/* 
   result := dyv describing where the contours between color 
            bands should lie. This is of size num_bands+1.

  *r_colors := of size num_bands. colors[0] is the color
               for heights between result[0]-result[1], etc.

               All heights are greater than result[0] and 
               less than result[num_bands] */
dyv *mk_sensible_dmap_threshes(dym *dmap,ivec **r_colors)
{
  int i = 1;
  int num_colors = NUM_GRAY_SCALES;
  dyv *ticks = mk_dyv(num_colors+1);
  dyv *depths = mk_dmap_dyv_from_all_dym_rows(dmap);
  dyv_sort(depths,depths);

  *r_colors = mk_ivec(num_colors);

  for ( i = 0 ; i < num_colors ; i++ )
  {
    int agcol = AG_GRAYSCALE_BASE + i;
    int index = (i==0) ? 0 : dyv_size(depths) * i / num_colors;
    double thresh = dyv_ref(depths,int_min(index,dyv_size(depths)-1));
    ivec_set(*r_colors,i,agcol);
    dyv_set(ticks,i,thresh);
  }
  dyv_set(ticks,num_colors,dyv_ref(depths,dyv_size(depths)-1));
	  
  free_dyv(depths);

  return ticks;
}

/* 
   result := dyv describing where the contours between color 
            bands should lie. This is of size num_bands+1.

  *r_colors := of size num_bands. colors[0] is the color
               for heights between result[0]-result[1], etc.

               All heights are greater than result[0] and 
               less than result[num_bands] */
dyv *mk_linear_dmap_threshes(dym *dmap,ivec **r_colors)
{
  int i = 1;
  int num_colors = NUM_GRAY_SCALES;
  dyv *ticks = mk_dyv(num_colors+1);
  double dmin = dym_min(dmap);
  double dmax = dym_max(dmap);

  *r_colors = mk_ivec(num_colors);

  for ( i = 0 ; i < num_colors ; i++ )
  {
    int agcol = AG_GRAYSCALE_BASE + i;
    double thresh = dmin + (dmax-dmin) * i / (double) num_colors;
    ivec_set(*r_colors,i,agcol);
    dyv_set(ticks,i,thresh);
  }
  dyv_set(ticks,num_colors,dmax);
	  
  return ticks;
}

imat *mk_color_agmap_from_dmap(dym *dmap,dyv *dmap_threshes,
			       ivec *colors)
{
  imat *agmap = mk_imat(dym_rows(dmap),dym_cols(dmap));
  int i,j;

  printf("Filling agmap... ");
  fflush(stdout);
  for ( i = 0 ; i < dym_rows(dmap) ; i++ )
    for ( j = 0 ; j < dym_cols(dmap) ; j++ )
    {  
      double v = dym_ref(dmap,i,j);
      int k = 0;
      int color;

      while ( k < dyv_size(dmap_threshes) && v > dyv_ref(dmap_threshes,k) )
	k++;
      color = ivec_ref(colors,int_max(0,k-1));
      imat_set(agmap,i,j,color);
    }
  printf("..finished\n");
  return agmap;
}

imat *mk_grayscale_agmap_from_dmap(dym *dmap)
{
  imat *agmap = mk_imat(dym_rows(dmap),dym_cols(dmap));
  int i,j;
  double maxv = dym_max(dmap);

  printf("Filling agmap... ");
  fflush(stdout);
  for ( i = 0 ; i < dym_rows(dmap) ; i++ )
    for ( j = 0 ; j < dym_cols(dmap) ; j++ )
    {  
      double v = dym_ref(dmap,i,j);
      double z = v * (NUM_GRAY_SCALES) / maxv;
      int ind = (int) floor(z + 0.5);
      int color;

      if ( ind < 0 ) ind = 0;
      if ( ind >= NUM_GRAY_SCALES ) ind = NUM_GRAY_SCALES-1;

      color = AG_GRAYSCALE_BASE + ind;

      imat_set(agmap,i,j,color);
    }
  printf("..finished\n");
  return agmap;
}

imat *mk_good_agmap_from_dmap(dym *dmap)
{
  dyv *values = mk_dmap_dyv_from_all_dym_rows(dmap);
  int num_colors = 2 + NUM_GRAY_SCALES;
  dyv *threshes = mk_dyv(num_colors-1);
  ivec *colors = mk_ivec(num_colors);
  dyv *nodup_values = mk_dyv(0);
  int i;
  imat *agmap;

  printf("Start sorting...\n");
  dyv_sort(values,values);
  printf("Remove duplicates...\n");
  for ( i = 0 ; i < dyv_size(values) ; i++ )
  {
    double v = dyv_ref(values,i);
    if ( i == 0 || dyv_ref(values,i-1) < v )
      add_to_dyv(nodup_values,v);
  }
  if ( dyv_size(nodup_values) < 30 )
    fprintf_dyv(stdout,"nodup_values",nodup_values,"\n");

  printf("Choose grey scales...\n");
  for ( i = 0 ; i < num_colors ; i++ )
  {
    int agcol = (i == 0) ? AG_WHITE :
                (i == num_colors-1) ? AG_BLACK :
                AG_GRAYSCALE_BASE + NUM_GRAY_SCALES - i;
    ivec_set(colors,i,agcol);
    if ( i < num_colors-1 )
    {
      double z = (i+1) / (double)(num_colors+1);
      int k = (int) floor(0.5 + dyv_size(nodup_values) * z);
      if ( k < 0 ) k = 0;
      if ( k >= dyv_size(nodup_values) ) k = dyv_size(nodup_values)-1;
      printf("i = %d, k = %d,z = %g\n",i,k,z);
      dyv_set(threshes,i,dyv_ref(nodup_values,k));
    }
  }

  fprintf_ivec(stdout,"colors",colors,"\n");
  fprintf_dyv(stdout,"threshes",threshes,"\n");

  printf("make matrix of colors...\n");

  agmap = mk_color_agmap_from_dmap(dmap,threshes,colors);

  free_dyv(values);
  free_dyv(threshes);
  free_ivec(colors);
  free_dyv(nodup_values);

  return agmap;
}

imat *mk_color_agmap_from_dmap_simple(dym *dmap)
{
  ivec *colors;
  dyv *dmap_threshes = mk_sensible_dmap_threshes(dmap,&colors);
  imat *agmap = mk_color_agmap_from_dmap(dmap,dmap_threshes,colors);
  free_dyv(dmap_threshes);
  free_ivec(colors);
  return agmap;
}

void render_grayscale_dmap(mapshape *ms,dym *dmap)
{
  int start_secs = global_time();
  imat *agmap = mk_grayscale_agmap_from_dmap(dmap);
  printf("Time to create grayscale dmap = %d secs\n",
         global_time() - start_secs);
  render_agmap(ms,agmap);
  free_imat(agmap);
}

void render_good_dmap(mapshape *ms,dym *dmap)
{
  int start_secs = global_time();
  imat *agmap = mk_good_agmap_from_dmap(dmap);
  printf("Time to create good dmap = %d secs\n",
         global_time() - start_secs);
  render_agmap(ms,agmap);
  free_imat(agmap);
}

void render_dmap_using_color(mapshape *ms,dym *dmap,dyv *dmap_threshes,
			     ivec *colors)
{
  int start_secs = global_time();
  imat *agmap = mk_color_agmap_from_dmap(dmap,dmap_threshes,colors);
  printf("Time to create color dmap = %d secs\n",global_time() - start_secs);
  render_agmap(ms,agmap);
  free_imat(agmap);
}

void render_color_dmap(mapshape *ms,dym *dmap)
{
  ivec *colors;
  /*  dyv *dmap_threshes = mk_sensible_dmap_threshes(dmap,&colors); */
  dyv *dmap_threshes = mk_linear_dmap_threshes(dmap,&colors);
  render_dmap_using_color(ms,dmap,dmap_threshes,colors);
  free_dyv(dmap_threshes);
  free_ivec(colors);
}


imat *mk_locmax_binmap(dym *dmap)
{
  int i,j;
  int size = dym_rows(dmap);
  imat *binmap = mk_zero_imat(size,size);
  for ( i = 0 ; i < size ; i++ )
    for ( j = 0 ; j < size ; j++ )
    {
      bool locmax = TRUE;
      double v = dym_ref(dmap,i,j);
      int mini = int_max(0,i-1);
      int maxi = int_min(size-1,i+1);
      int minj = int_max(0,j-1);
      int maxj = int_min(size-1,j+1);
      int ii,jj;
      for ( ii = mini ; locmax && ii <= maxi ; ii++ )
	for ( jj = minj ; locmax && jj <= maxj ; jj++ )
	  if ( ii != i && jj != j && dym_ref(dmap,ii,jj) >= v ) locmax = FALSE;

      if ( locmax )
	imat_set(binmap,i,j,1);
    }
  return binmap;
}

imat *mk_ridge_binmap(dym *dmap)
{
  int i,j;
  int size = dym_rows(dmap);
  imat *binmap = mk_zero_imat(size,size);
  for ( i = 0 ; i < size ; i++ )
    for ( j = 0 ; j < size ; j++ )
    {
      bool ridge = FALSE;
      if ( i > 0 && j > 0 && i < size-1 && j < size-1 )
      {
        double v = dym_ref(dmap,i,j);
	if ( dym_ref(dmap,i-1,j) < v && dym_ref(dmap,i+1,j) < v )
	  ridge = TRUE;
	if ( dym_ref(dmap,i,j-1) < v && dym_ref(dmap,i,j+1) < v )
	  ridge = TRUE;
      }

      if ( ridge )
	imat_set(binmap,i,j,1);
    }
  return binmap;
}

void smooth_binmap(imat *binmap)
{
  int i,j;
  int size = imat_rows(binmap);
  imat *new_binmap = mk_zero_imat(size,size);
  for ( i = 0 ; i < size ; i++ )
    for ( j = 0 ; j < size ; j++ )
    {
      bool neighon = imat_ref(binmap,i,j) > 0;
      int mini = int_max(0,i-1);
      int maxi = int_min(size-1,i+1);
      int minj = int_max(0,j-1);
      int maxj = int_min(size-1,j+1);
      int ii,jj;
      for ( ii = mini ; !neighon && ii <= maxi ; ii++ )
	for ( jj = minj ; !neighon && jj <= maxj ; jj++ )
	  if ( imat_ref(binmap,ii,jj) > 0 ) neighon = TRUE;

      if ( neighon )
	imat_set(new_binmap,i,j,1);
    }
  copy_imat(new_binmap,binmap);
  printf("imat_max(binmap) = %d\n",imat_max(binmap));
  free_imat(new_binmap);
}

void shrink_binmap_delta(imat *binmap,int di,int dj)
{
  int i,j;
  int size = imat_rows(binmap);
  imat *new_binmap = mk_copy_imat(binmap);
  for ( i = di ; i < size ; i += 2 )
    for ( j = dj ; j < size ; j += 2 )
    {
      if ( imat_ref(binmap,i,j) > 0 )
      {
	int count = 0;
	int mini = int_max(0,i-1);
	int maxi = int_min(size-1,i+1);
	int minj = int_max(0,j-1);
	int maxj = int_min(size-1,j+1);
	int ii,jj;
	for ( ii = mini ; ii <= maxi ; ii++ )
	  for ( jj = minj ; jj <= maxj ; jj++ )
	    if ( imat_ref(binmap,ii,jj) > 0 ) count += 1;

	if ( count > 6 && count < 9 )
	  imat_set(new_binmap,i,j,0);
      }
    }
  copy_imat(new_binmap,binmap);
  printf("imat_max(binmap) = %d\n",imat_max(binmap));
  free_imat(new_binmap);
}

void shrink_binmap(imat *binmap)
{
  int di,dj;
  for ( di = 0 ; di <= 1 ; di++ )
    for ( dj = 0 ; dj <= 1 ; dj++ )
      shrink_binmap_delta(binmap,di,dj);
}

void insert_binmap_into_agmap(imat *agmap,imat *binmap,int agcol)
{
  int size = imat_rows(agmap);
  int i,j;
  for ( i = 0 ; i < size ; i++ )
    for ( j = 0 ; j < size ; j++ )
      if ( imat_ref(binmap,i,j) > 0 )
	imat_set(agmap,i,j,agcol);
}

void render_peak_dmap(mapshape *ms,dym *dmap)
{
  imat *agmap = mk_color_agmap_from_dmap_simple(dmap);
  imat *binmap = mk_locmax_binmap(dmap);
  int num_smooths = 1;
  int i;

  for ( i = 0 ; i < num_smooths ; i++ )
    smooth_binmap(binmap);

  insert_binmap_into_agmap(agmap,binmap,AG_BLACK);

  render_agmap(ms,agmap);
  free_imat(agmap);
  free_imat(binmap);
}

void render_ridge_dmap(mapshape *ms,dym *dmap)
{
  imat *agmap = mk_color_agmap_from_dmap_simple(dmap);
  imat *binmap = mk_ridge_binmap(dmap);
  int num_smooths = 1;
  int i;

  for ( i = 0 ; i < num_smooths ; i++ )
    smooth_binmap(binmap);

  insert_binmap_into_agmap(agmap,binmap,AG_BLACK);

  render_agmap(ms,agmap);
  free_imat(agmap);
  free_imat(binmap);
}

void render_dmap(mapshape *ms,dym *dmap,int dmap_type,double frac)
{
  switch ( dmap_type )
  {
    case REGULAR_DMAP: regular_render_dmap(ms,dmap,frac); break;
    case ANIMATE_DMAP: animate_dmap(ms,dmap); break;
    case COLOR_DMAP: render_color_dmap(ms,dmap); break;
    case GRAYSCALE_DMAP: render_grayscale_dmap(ms,dmap); break;
    case GOOD_DMAP: render_good_dmap(ms,dmap); break;
    case PEAK_DMAP: render_peak_dmap(ms,dmap); break;
    case RIDGE_DMAP: render_ridge_dmap(ms,dmap); break;
    default: my_error("Unknown DMAP_TYPE"); break;
  }
}

void ms_dot(mapshape *ms,double x,double y)
{
  ms_mark(ms,x,y,DOT_MARKTYPE);
}

void ms_mark(mapshape *ms,double x,double y,int marktype)
{
  double agx,agy;
  mapshape_datapoint_to_agcoords(ms,x,y,&agx,&agy);
  ag_mark(agx,agy,marktype);
}

void ms_line(mapshape *ms,double x1,double y1,double x2,double y2)
{
  double agx1,agy1;
  double agx2,agy2;
  mapshape_datapoint_to_agcoords(ms,x1,y1,&agx1,&agy1);
  mapshape_datapoint_to_agcoords(ms,x2,y2,&agx2,&agy2);
  ag_line(agx1,agy1,agx2,agy2);
}

void ms_disc(mapshape *ms,double x,double y,double ag_rad)
{
  double agx,agy;
  mapshape_datapoint_to_agcoords(ms,x,y,&agx,&agy);
  ag_disc(agx,agy,ag_rad);
}

void ms_circle(mapshape *ms,double x,double y,double ag_rad)
{
  double agx,agy;
  mapshape_datapoint_to_agcoords(ms,x,y,&agx,&agy);
  ag_circle(agx,agy,ag_rad);
}

void ms_rectangle(mapshape *ms,double x1,double y1,double x2,double y2)
{
  double agx1,agy1;
  double agx2,agy2;
  mapshape_datapoint_to_agcoords(ms,x1,y1,&agx1,&agy1);
  mapshape_datapoint_to_agcoords(ms,x2,y2,&agx2,&agy2);
  ag_rectangle(agx1,agy1,agx2,agy2);
}

void ms_hrect(mapshape *ms,hrect *hr)
{
  ms_rectangle(ms,hrect_lo_ref(hr,0),hrect_lo_ref(hr,1),
	       hrect_hi_ref(hr,0),hrect_hi_ref(hr,1));
}

void ms_hrect_colored(mapshape *ms,hrect *hr,int agcol)
{
  int col = ag_pen_color();
  ag_set_pen_color(agcol);
  ms_hrect(ms,hr);
  ag_set_pen_color(col);
}

void ms_box(mapshape *ms,double x1,double y1,double x2,double y2)
{
  double agx1,agy1;
  double agx2,agy2;
  mapshape_datapoint_to_agcoords(ms,x1,y1,&agx1,&agy1);
  mapshape_datapoint_to_agcoords(ms,x2,y2,&agx2,&agy2);
  ag_box(agx1,agy1,agx2,agy2);
}

void ms_hrect_box(mapshape *ms,hrect *hr)
{
  ms_box(ms,hrect_lo_ref(hr,0),hrect_lo_ref(hr,1),
	 hrect_hi_ref(hr,0),hrect_hi_ref(hr,1));
}

void ms_hrect_box_colored(mapshape *ms,hrect *hr,int agcol)
{
  int col = ag_pen_color();
  ag_set_pen_color(agcol);
  ms_hrect_box(ms,hr);
  ag_set_pen_color(col);
}

int ms_get_xy(mapshape *ms,double *r_x,double *r_y)
{
  double agx,agy;
  int button = ag_get_xy(&agx,&agy);
  mapshape_agcoords_to_datapoint(ms,agx,agy,r_x,r_y);
  return button;
}

void ms_dyv(mapshape *ms,dyv *x,int marktype)
{
  ms_mark(ms,dyv_ref(x,0),dyv_ref(x,1),marktype);
}

void ms_dyv_colored(mapshape *ms,dyv *x,int marktype,int agcol)
{
  int col = ag_pen_color();
  ag_set_pen_color(agcol);
  ms_dyv(ms,x,marktype);
  ag_set_pen_color(col);
}

void ms_point_in_dym_colored(mapshape *ms,dym *x,int row,
			     int marktype,int agcol)
{
  dyv *vec = mk_dyv_from_dym_row(x,row);
  ms_dyv_colored(ms,vec,marktype,agcol);
  free_dyv(vec);
}

void ms_dym_colored(mapshape *ms,dym *x,int marktype,int agcol)
{
  int i;
  for ( i = 0 ; i < dym_rows(x) ; i++ )
    ms_point_in_dym_colored(ms,x,i,marktype,agcol);
}
    
void ms_metric_circle(mapshape *ms,dyv *metric,dyv *center,double radius)
{
  double xprev = -77.77;
  double yprev = -77.77;
  int i;
  int size = 32;
  for ( i = 0 ; i <= size ; i++ )
  {
    double theta = 2.0 * PI * (double) i / (double) size;
    double x = dyv_ref(center,0) + dyv_ref(metric,0) * radius * cos(theta);
    double y = dyv_ref(center,1) + dyv_ref(metric,1) * radius * sin(theta);
    if ( i > 0 )
      ms_line(ms,x,y,xprev,yprev);
    xprev = x;
    yprev = y;
  }
}

void ms_metric_circle_colored(mapshape *ms,dyv *metric,dyv *center,
			      double radius,int agcol)
{
  int col = ag_pen_color();
  ag_set_pen_color(agcol);
  ms_metric_circle(ms,metric,center,radius);
  ag_set_pen_color(col);
}

void ms_dyv_line(mapshape *ms,dyv *x,dyv *y)
{
  ms_line(ms,dyv_ref(x,0),dyv_ref(x,1),dyv_ref(y,0),dyv_ref(y,1));
}

void ms_print(mapshape *ms,double x,double y,char *string)
{
  double agx,agy;
  mapshape_datapoint_to_agcoords(ms,x,y,&agx,&agy);
  ag_print(agx,agy,string);
}

/* Returns NULL if right button pressed */
dyv *mk_query_from_ms(mapshape *ms)
{
  int button;
  double x,y;
  printf("Click Mouse on Point (right button to quit)\n");
  button = ms_get_xy(ms,&x,&y);
  return (button==3) ? NULL : mk_dyv_2(x,y);
}

/* Returns NULL if right button pressed */
dyv *mk_query_and_radius_from_ms(mapshape *ms,dyv *metric,double *radius)
{
  dyv *query;
  
  *radius = -77.77;

  printf("Click at center of query\n");
  query = mk_query_from_ms(ms);
  if ( query != NULL )
  {
    dyv *edge;
    ms_dyv_colored(ms,query,FANCY_MARKTYPE,AG_BLUE);
    printf("Click at radius of query radio\n");
    edge = mk_query_from_ms(ms);
    if ( edge == NULL )
    {
      free_dyv(query);
      query = NULL;
    }
    else
      *radius = sqrt(metric_dsqd(metric,query,edge));

    if ( edge != NULL )
      free_dyv(edge);
  }

  return query;
}

void ms_string(mapshape *ms,double x,double y,char *s)
{
  double agx,agy;
  mapshape_datapoint_to_agcoords(ms,x,y,&agx,&agy);
  ag_print(agx,agy,s);
}

void ms_string_colored(mapshape *ms,double x,double y,char *s,int agcol)
{
  int col = ag_pen_color();
  ag_set_pen_color(agcol);
  ms_string(ms,x,y,s);
  ag_set_pen_color(col);
}

void ms_dyv_string_colored(mapshape *ms,dyv *x,char *s,int agcol)
{
  ms_string_colored(ms,dyv_ref(x,0),dyv_ref(x,1),s,agcol);
}

void ms_point_in_dym_colored_string(mapshape *ms,dym *x,int row,char *s,
				    int agcol)
{
  dyv *xv = mk_dyv_from_dym_row(x,row);
  ms_dyv_string_colored(ms,xv,s,agcol);
  free_dyv(xv);
}

void ms_line_between_dym_rows(mapshape *ms,dym *x1,dym *x2,
			      int row1,int row2,int agcol)
{
  dyv *x1v = mk_dyv_from_dym_row(x1,row1);
  dyv *x2v = mk_dyv_from_dym_row(x2,row2);
  ag_set_pen_color(agcol);
  ms_dyv_line(ms,x1v,x2v);
  free_dyv(x1v);
  free_dyv(x2v);
}

dym *mk_dmap_from_heights(dym *heights,int drows,int dcols)
{
  dym *dmap = mk_dym(drows,dcols);
  int hrows = dym_rows(heights);
  int hcols = dym_cols(heights);
  if ( hrows == drows && hcols == dcols )
  {
    copy_dym(heights,dmap);
    printf("Nothing fancy needed\n");
  }
  else
  {
    int di,dj;
    for ( di = 0 ; di < drows ; di++ )
    {
      int hi_lo = int_max(0,int_min(hrows-1,(int) floor(hrows * di / (double)(drows-1))));
      int di_lo = int_max(0,int_min(drows-1,(int) floor(drows * hi_lo / (double)(hrows-1))));
      int di_hi = int_max(0,int_min(drows-1,(int) floor(drows * (hi_lo+1) / (double)(hrows-1))));
      int hi_hi = int_min(hi_lo+1,hrows-1);
      double wi = (di - di_lo)/real_max(1.0,(double)(di_hi-di_lo));
      
      for ( dj = 0 ; dj < dcols ; dj++ )
      {
  	    int hj_lo = int_max(0,int_min(hcols-1,(int) floor(hcols * dj / (double)(dcols-1))));
	      int dj_lo = int_max(0,int_min(dcols-1,(int) floor(dcols * hj_lo / (double)(hcols-1))));
	      int dj_hi = int_max(0,int_min(dcols-1,(int) floor(dcols * (hj_lo+1) / (double)(hcols-1))));
	      int hj_hi = int_min(hj_lo+1,hcols-1);
	      double wj = (dj - dj_lo)/real_max(1.0,(double)(dj_hi-dj_lo));
	      double v = (1.0 - wi) * (1.0 - wj) * dym_ref(heights,hi_lo,hj_lo) +
	                 wi         * (1.0 - wj) * dym_ref(heights,hi_hi,hj_lo) +
	                (1.0 - wi) *        wj  * dym_ref(heights,hi_lo,hj_hi) +
	                wi         *        wj  * dym_ref(heights,hi_hi,hj_hi);
	      dym_set(dmap,di,dj,v);
      }
    }
  }
  return dmap;
}

void render_heights(mapshape *ms,dym *heights,int dmap_type)
{
  dym *dmap = mk_dmap_from_heights(heights,ms->agrows,ms->agcols);
  render_dmap(ms,dmap,dmap_type,0.5);
  free_dym(dmap);
}

string_array *mk_labels_for_color_bands(dyv *dmap_threshes)
{
  int size = dyv_size(dmap_threshes)-1;
  string_array *sa = mk_string_array(size);
  int i;
  for ( i = 0 ; i < size ; i++ )
  {
    char *s = mk_printf("(%g,%g)",dyv_ref(dmap_threshes,i),
			dyv_ref(dmap_threshes,i+1));
    string_array_set(sa,i,s);
    free_string(s);
  }
  return sa;
}

hrect *mk_hrect_from_agbox(agbox *ag)
{
  dyv *lo = mk_dyv_2(agbox_ref(ag,XCOMP,LO),agbox_ref(ag,YCOMP,LO));
  dyv *hi = mk_dyv_2(agbox_ref(ag,XCOMP,HI),agbox_ref(ag,YCOMP,HI));
  hrect *hr = mk_hrect(lo,hi);
  free_dyv(lo);
  free_dyv(hi);
  return hr;
}

mapshape *mk_mapshape_from_hrect_and_agbox(hrect *hr,agbox *ag)
{
  mapshape *ms = AM_MALLOC(mapshape);
  ms -> data_bound = mk_copy_hrect(hr);
  ms -> ag_bound = mk_hrect_from_agbox(ag);
  ms -> agrows = (int)agbox_width(ag,YCOMP);
  ms -> agcols = (int)agbox_width(ag,XCOMP);
  return ms;
}

mapshape *mk_heights_mapshape(hrect *hr,dym *heights,
			      char *x_attname,char *y_attname,char *z_attname)
{
  agbox *ag = mk_full_screen_agbox();
  char *s = mk_printf("Height of %s as a function of %s and %s",
		      z_attname,x_attname,y_attname);
  ivec *colors;
  dyv *dmap_threshes = mk_sensible_dmap_threshes(heights,&colors);
  string_array *band_labels = mk_labels_for_color_bands(dmap_threshes);
  dym *dmap;
  agbox *vaxis;
  agbox *not_vaxis;
  agbox *haxis;
  agbox *data;
  mapshape *ms;

  set_ag_frame(0.0,0.0,512.0,512.0);
  ag_set_pen_color(AG_GRAY);
  ag_box(0.0,0.0,512.0,512.0);
  ag_set_pen_color(AG_WHITE);
  print_and_shrink(ag,s);
  print_color_key_and_shrink_special_colors(ag,band_labels,colors);

  split_agbox_vertical_absolute(ag,50.0,&vaxis,&not_vaxis);
  split_agbox_horizontal_absolute(not_vaxis,50.0,&haxis,&data);
  agbox_set(data,XCOMP,LO,agbox_ref(data,XCOMP,HI)-agbox_width(data,YCOMP));

  ms = mk_mapshape_from_hrect_and_agbox(hr,data);

  printf("(%d,%d)\n",ms->agrows,ms->agcols);

  dmap = mk_dmap_from_heights(heights,ms->agrows,ms->agcols);


  ag_set_pen_color(AG_WHITE);
  agbox_render_string_at_bottom(haxis,x_attname);
  agbox_render_string_vertical(vaxis,y_attname);
  agbox_fill(data);

  render_dmap(ms,dmap,COLOR_DMAP,0.5);

  free_agbox(ag);
  free_string(s);
  free_dyv(dmap_threshes);
  free_ivec(colors);
  free_string_array(band_labels);
  free_dym(dmap);
  free_agbox(vaxis);
  free_agbox(not_vaxis);
  free_agbox(haxis);
  free_agbox(data);

  ag_set_pen_color(AG_BLACK);

  return ms;
}

dym_array *mk_dmaps_from_multi_heights(dym_array *heights,int rows,int cols)
{
  dym_array *dmaps = mk_empty_dym_array();
  int i;
  for ( i = 0 ; i < dym_array_size(heights) ; i++ )
  {
    dym *dmap = mk_dmap_from_heights(dym_array_ref(heights,i),rows,cols);
    add_to_dym_array(dmaps,dmap);
    free_dym(dmap);
  }
  return dmaps;
}

/* Makes x such that dyv_size(x) = dym_array_size(da)
   and forall 0 <= k < size,

    dyv_ref(x,k) = dym_ref(da_k,i,j) where da_k = dym_array_ref(da,k)
*/
dyv *mk_dyv_from_dym_array_location(dym_array *da,int i,int j)
{
  int size = dym_array_size(da);
  int k;
  dyv *x = mk_dyv(size);
  for ( k = 0 ; k < size ; k++ )
  {
    dym *da_k = dym_array_ref(da,k);
    double da_kij = dym_ref(da_k,i,j);
    dyv_set(x,k,da_kij);
  }
  return x;
}

/* Returns an imat in which a pixels color depends on the predicted
   class */
imat *mk_agmap_from_dmaps(dym_array *heights)
{
  int rows = dym_rows(dym_array_ref(heights,0));
  int cols = dym_cols(dym_array_ref(heights,0));
  int i,j;
  imat *agmap = mk_imat(rows,cols);

  printf("Deciding on prediction color map...\n");

  for ( i = 0 ; i < rows ; i++ )
    for ( j = 0 ; j < cols ; j++ )
    {
      dyv *cpvec = mk_dyv_from_dym_array_location(heights,i,j);
      int class = dyv_argmax(cpvec);
      int agcol = color_code_to_ag_color(class);
      imat_set(agmap,i,j,agcol);
      free_dyv(cpvec);
    }

  printf("...finished\n");
  return agmap;
}

imat *mk_hollow_agmap(imat *agmap)
{
  int size = imat_rows(agmap);
  int i,j;
  imat *hollow = mk_copy_imat(agmap);
  printf("Hollowing a picture...\n");

  for ( i = 0 ; i < size ; i++ )
    for ( j = 0 ; j < size ; j++ )
    {
      bool allsame = TRUE;
      int col = imat_ref(agmap,i,j);

      if (i-1 >= 0 && j-1 >= 0 && col != imat_ref(agmap,i-1,j-1) ) 
	allsame = FALSE;
      if (i+1 < size && j-1 >= 0 && col != imat_ref(agmap,i+1,j-1) ) 
	allsame = FALSE;
      if (i-1 >= 0 && j+1 < size && col != imat_ref(agmap,i-1,j+1) ) 
	allsame = FALSE;
      if (i+1 < size && j+1 < size && col != imat_ref(agmap,i+1,j+1) ) 
	allsame = FALSE;

      if ( allsame )
	imat_set(hollow,i,j,AG_WHITE);
    }

  printf("... finished\n");

  return hollow;
}

imat *mk_expand_agmap(imat *agmap)
{
  int size = imat_rows(agmap);
  int i,j;
  imat *expand = mk_copy_imat(agmap);

  printf("Expanding a picture...\n");
  for ( i = 0 ; i < size ; i++ )
    for ( j = 0 ; j < size ; j++ )
      if ( imat_ref(agmap,i,j) == AG_WHITE )
      {
	int col = AG_WHITE;

	if (i-1 >= 0 && j-1 >= 0 && AG_WHITE != imat_ref(agmap,i-1,j-1) ) 
	  col = imat_ref(agmap,i-1,j-1);
	if (i+1 < size && j-1 >= 0 && AG_WHITE != imat_ref(agmap,i+1,j-1) )
	  col = imat_ref(agmap,i+1,j-1);
	if (i-1 >= 0 && j+1 < size && AG_WHITE != imat_ref(agmap,i-1,j+1) )
	  col = imat_ref(agmap,i-1,j+1);
	if (i+1 < size && j+1 < size && AG_WHITE != imat_ref(agmap,i+1,j+1) )
	  col = imat_ref(agmap,i+1,j+1);
	if ( col != AG_WHITE )
	  imat_set(expand,i,j,col);
      }
  printf("... finished\n");

  return expand;
}

void draw_class_borders(mapshape *ms,imat *agmap)
{
  imat *hollow = mk_hollow_agmap(agmap);
  imat *expand = mk_expand_agmap(hollow);
  imat *expand2 = mk_expand_agmap(expand);
  render_agmap(ms,expand2);
  free_imat(hollow);
  free_imat(expand);
  free_imat(expand2);
}

void render_dmaps(mapshape *ms,dym_array *dmaps)
{
  imat *agmap = mk_agmap_from_dmaps(dmaps);
  draw_class_borders(ms,agmap);
  free_imat(agmap);
}

mapshape *mk_multi_heights_mapshape(hrect *hr,dym_array *heights,
				    char *x_attname,char *y_attname,
				    char *z_attname,
				    string_array *z_valnames)
{
  agbox *ag = mk_full_screen_agbox();
  char *s = mk_printf("Class of of %s as a function of %s and %s",
		      z_attname,x_attname,y_attname);
  dym_array *dmaps;
  agbox *vaxis;
  agbox *not_vaxis;
  agbox *haxis;
  agbox *data;
  mapshape *ms;

  set_ag_frame(0.0,0.0,512.0,512.0);
  ag_set_pen_color(AG_GRAY);
  ag_box(0.0,0.0,512.0,512.0);
  ag_set_pen_color(AG_WHITE);
  print_and_shrink(ag,s);

  split_agbox_vertical_absolute(ag,50.0,&vaxis,&not_vaxis);
  split_agbox_horizontal_absolute(not_vaxis,50.0,&haxis,&data);
  agbox_set(data,XCOMP,LO,agbox_ref(data,XCOMP,HI)-agbox_width(data,YCOMP));

  ms = mk_mapshape_from_hrect_and_agbox(hr,data);

  printf("(%d,%d)\n",ms->agrows,ms->agcols);

  dmaps = mk_dmaps_from_multi_heights(heights,ms->agrows,ms->agcols);

  ag_set_pen_color(AG_WHITE);
  agbox_render_string_at_bottom(haxis,x_attname);
  agbox_render_string_vertical(vaxis,y_attname);
  agbox_fill(data);

  render_dmaps(ms,dmaps);

  free_agbox(ag);
  free_string(s);
  free_dym_array(dmaps);
  free_agbox(vaxis);
  free_agbox(not_vaxis);
  free_agbox(haxis);
  free_agbox(data);

  ag_set_pen_color(AG_BLACK);

  return ms;
}


