/*
   File:        lingraph.c
   Author:      Andrew W. Moore
   Created:     Wed May  8 19:16:36 EDT 1996
   Description: Graphs with lines on them

   Copyright (C) 1996, A. W. Moore
*/

#include "lingraph.h"
#include "ongr.h"
#include "awdut.h"

/************ rspart bits first. This is specific to drawing 
              real-symbolic histograms ************/

rspart *mk_empty_rspart(void)
{
  rspart *x = AM_MALLOC(rspart);
  x -> data = mk_empty_dyv_array();
  x -> counts = mk_empty_dyv_array();
  x -> num_bars = -1; /* Denotes undecided */
  x -> ratio = FALSE;
  x -> labels = mk_string_array(0);
  return(x);
}

void fprintf_rspart(FILE *s,char *m1,rspart *rsp,char *m2)
{
  char *buff;
  buff = mk_printf("%s -> data",m1);
  fprintf_dyv_array(s,buff,rsp->data,m2);
  free_string(buff);

  buff = mk_printf("%s -> counts",m1);
  fprintf_dyv_array(s,buff,rsp->counts,m2);
  free_string(buff);

  buff = mk_printf("%s -> num_bars",m1);
  fprintf_int(s,buff,rsp->num_bars,m2);
  free_string(buff);

  buff = mk_printf("%s -> ratio",m1);
  fprintf_bool(s,buff,rsp->ratio,m2);
  free_string(buff);

  buff = mk_printf("%s -> labels",m1);
  fprintf_string_array(s,buff,rsp->labels,m2);
  free_string(buff);
}

void free_rspart(rspart *x)
{
  if ( x->data != NULL ) free_dyv_array(x->data);
  if ( x->counts != NULL ) free_dyv_array(x->counts);
  if ( x->labels != NULL ) free_string_array(x->labels);
  AM_FREE(x,rspart);
}

rspart *mk_copy_rspart(rspart *x)
{
  rspart *nx = AM_MALLOC(rspart);
  nx->data = (x->data==NULL) ? NULL : mk_copy_dyv_array(x->data);
  nx->counts = (x->counts==NULL) ? NULL : mk_copy_dyv_array(x->counts);
  nx->num_bars = x -> num_bars;
  nx->ratio = x -> ratio;
  nx->labels = (x->labels==NULL) ? NULL : mk_copy_string_array(x->labels);
  return(nx);
}

void maybe_expand_num_classes(rspart *rsp,int classnum)
{
  if ( classnum < 0 || classnum > 1000 )
    my_error("rspart classnums should be between 0 -- 1000");
  while ( dyv_array_size(rsp->data) <= classnum )
  {
    dyv *new_classnum = mk_dyv(0);
    add_to_dyv_array(rsp->data,new_classnum);
    add_to_dyv_array(rsp->counts,new_classnum);
    add_to_string_array(rsp->labels,NULL);
    free_dyv(new_classnum);
  }
}

int rspart_num_classes(rspart *rsp)
{
  return(dyv_array_size(rsp->data));
}

dyv *rspart_class_to_data(rspart *rsp,int classnum)
{
  maybe_expand_num_classes(rsp,classnum);
  return(dyv_array_ref(rsp->data,classnum));
}

dyv *rspart_class_to_counts(rspart *rsp,int classnum)
{
  maybe_expand_num_classes(rsp,classnum);
  return(dyv_array_ref(rsp->counts,classnum));
}

void add_to_rspart(rspart *rsp,double x,double count,int classnum)
{
  dyv *data = rspart_class_to_data(rsp,classnum);
  dyv *counts = rspart_class_to_counts(rsp,classnum);
  add_to_dyv(data,x);
  add_to_dyv(counts,count);
}

void rspart_set_class_label(rspart *rsp,int classnum,char *label)
{
  maybe_expand_num_classes(rsp,classnum);
  string_array_set(rsp->labels,classnum,label);
}

void rspart_set_num_bars(rspart *rsp,int num_bars)
{
  rsp -> num_bars = num_bars;
}

void rspart_show_ratio(rspart *rsp)
{
  rsp -> ratio = TRUE;
}

int dyv_array_sum_sizes(dyv_array *dva)
{
  int result = 0;
  int i;
  for ( i = 0 ; i < dyv_array_size(dva) ; i++ )
    result += dyv_size(dyv_array_ref(dva,i));
  return result;
}

double dyv_array_min(dyv_array *dva)
{
  double result = -777.77;
  bool found = FALSE;
  int i;
  for ( i = 0 ; i < dyv_array_size(dva) ; i++ )
  {
    dyv *dv = dyv_array_ref(dva,i);
    if ( dyv_size(dv) > 0 )
    {
      double dvm = dyv_min(dv);
      if ( !found )
      {
	result = dvm;
	found = TRUE;
      }
      else
	result = real_min(result,dvm);
    }
  }

  return result;
}

double dyv_array_max(dyv_array *dva)
{
  double result = -777.77;
  bool found = FALSE;
  int i;
  for ( i = 0 ; i < dyv_array_size(dva) ; i++ )
  {
    dyv *dv = dyv_array_ref(dva,i);
    if ( dyv_size(dv) > 0 )
    {
      double dvm = dyv_max(dv);
      if ( !found )
      {
	result = dvm;
	found = TRUE;
      }
      else
	result = real_max(result,dvm);
    }
  }

  return result;
}

int dyv_array_max_size(dyv_array *dva)
{
  int result = -777;
  int i;
  for ( i = 0 ; i < dyv_array_size(dva) ; i++ )
  {
    dyv *dv = dyv_array_ref(dva,i);
    if ( dv != NULL)
      result = int_max(result,dyv_size(dv));

  }

  return result;
}


int rspart_num_points(rspart *rsp)
{
  return dyv_array_sum_sizes(rsp->data);
}

int rspart_num_bars(rspart *rsp)
{
  int result;
  if ( rsp -> num_bars > 0 )
    result = rsp -> num_bars;
  else
    result = int_max(2,int_min(60,(int)sqrt((double)rspart_num_points(rsp))));
  return result;
}

int lingraph_num_classes(lingraph *lg)
{
  int result = 1;
  if ( lg->rsp != NULL )
    result = dyv_array_size(lg->rsp->data);
  else if ( lg->ssp != NULL )
    result = dym_cols(lg->ssp->freqs);
    
  return result;
}

/* Result[i][j] = number in the j'th classnum and in the ith bar 
   dyv_rows(result) = num_bars
   dyv_cols(result) = num_classes */
dym *mk_freqs_dym(dyv_array *data,dyv_array *counts,
		  int num_bars,double xlo,double xhi)
{
  int num_classes = dyv_array_size(data);
  dym *result = mk_zero_dym(num_bars,num_classes);
  int i;
  for ( i = 0 ; i < num_classes ; i++ )
  {
    dyv *dv = dyv_array_ref(data,i);
    dyv *count_vector = dyv_array_ref(counts,i);
    int j;
    for ( j = 0 ; j < dyv_size(dv) ; j++ )
    {
      double x = dyv_ref(dv,j);
      double z = (x-xlo)/(xhi-xlo);
      int bar_num = (int)floor(z * num_bars);
      if ( bar_num < 0 ) bar_num = 0;
      if ( bar_num >= num_bars ) bar_num = num_bars-1;
      dym_increment(result,bar_num,i,dyv_ref(count_vector,j));
    }
  }
  return result;
}

double dym_max_row_sum(dym *x)
{
  dyv *row_sums = mk_sum_of_dym_cols(x);
  double result = dyv_max(row_sums);
  free_dyv(row_sums);
  return result;
}

int lingraph_rs_max_bar_height(lingraph *lg)
{
  double hi, lo;
  int max = -1, i;
  dym *freqs;
  dym *mk_freqs_from_rspart(rspart *rsp,double *r_xlo,double *r_xhi);

  if(lg->rsp==NULL)
    return 0;


  freqs = mk_freqs_from_rspart(lg->rsp,&lo,&hi);

  for(i = 0; i < dym_cols(freqs); i++)
  {
    int result = (int) dym_col_max(freqs, i);
    if(result > max)
      max = result;
  }

  free_dym(freqs);
  return max;
}

double lingraph_rs_max_bar_width(lingraph *lg)
{
  return (lg->rsp==NULL) ? 0.0 : (dyv_array_max(lg->rsp->data) - dyv_array_min(lg->rsp->data));
}

dym *mk_freqs_from_rspart(rspart *rsp,double *r_xlo,double *r_xhi)
{
  double xlo = dyv_array_min(rsp->data);
  double xhi = dyv_array_max(rsp->data);
  int num_bars = rspart_num_bars(rsp);
  dym *freqs = mk_freqs_dym(rsp->data,rsp->counts,num_bars,xlo,xhi);

  *r_xlo = xlo;
  *r_xhi = xhi;

  return freqs;
}

double get_yhi_from_freqs(dym *freqs,bool ratio)
{
  double yhi = (ratio) ? 120.0 : dym_max_row_sum(freqs);
  return yhi;
}

agbox *mk_agbox_surrounding_rspart(rspart *rsp)
{
  double xlo;
  double xhi;
  dym *freqs = mk_freqs_from_rspart(rsp,&xlo,&xhi);
  double yhi = get_yhi_from_freqs(freqs,rsp->ratio);
  agbox *image = mk_agbox(xlo,0.0,xhi,yhi);

  free_dym(freqs);
  return image;
}

void maybe_expand_image_from_rspart(agbox **r_image,rspart *rsp)
{
  if ( rsp != NULL )
  {
    agbox *image = mk_agbox_surrounding_rspart(rsp);
    if ( *r_image == NULL )
      *r_image = mk_copy_agbox(image);
    else
      maybe_expand_agbox_from_agbox(*r_image,image);
    free_agbox(image);
  }
}

void render_rspart(map *mp,rspart *rsp)
{
  int old_color = ag_pen_color();
  double xlo,xhi;
  dym *freqs = mk_freqs_from_rspart(rsp,&xlo,&xhi);

  ongr_plot_hist(mp,freqs,rsp->ratio);
  ag_set_pen_color(old_color);

  free_dym(freqs);
}

rspart *rspart_ref_maybe_create(lingraph *lg)
{
  if ( lg->rsp == NULL )
    lg -> rsp = mk_empty_rspart();
  return lg->rsp;
}

/* BEGIN THE API THAT LINGRAPH CREATORS GET TO SEE */

/* Adds to the "RS" (real-symbolic) part of the lingraph. 
   Remember, RS part is a histogram indexed by a real
   axis, where bars can be composed of more than one color
   according to their class. */
void lingraph_rs_add(lingraph *lg,double x,double count,int classnum)
{
  add_to_rspart(rspart_ref_maybe_create(lg),x,count,classnum);
}

/* Gives a name to the classnum'th bar color in the RS part.
   Remember, RS part is a histogram indexed by a real
   axis, where bars can be composed of more than one color
   according to their class. */
void lingraph_rs_set_class_label(lingraph *lg,int classnum,char *label)
{
  rspart_set_class_label(rspart_ref_maybe_create(lg),classnum,label);
}

/* Forces the number of bars to be used in the RS part. Note
   it's not necessary to call this..the code will figure out
   a suitable default if you don't.
   Remember, RS part is a histogram indexed by a real
   axis, where bars can be composed of more than one color
   according to their class. */
void lingraph_rs_set_num_bars(lingraph *lg,int num_bars)
{
  rspart_set_num_bars(rspart_ref_maybe_create(lg),num_bars);
}

/* instructs the RS part to scale all bars to the same heights
   so that the ratios of color sizes in each bar can be inspected.
   Remember, RS part is a histogram indexed by a real
   axis, where bars can be composed of more than one color
   according to their class. */
void lingraph_rs_show_ratio(lingraph *lg)
{
  rspart_show_ratio(rspart_ref_maybe_create(lg));
}

/*********** End of rspart stuff ************/

/*********** Begin symbolic-symbolic stuff ********/

sspart *mk_empty_sspart(void)
{
  sspart *x = AM_MALLOC(sspart);
  x -> freqs = mk_dym(0,0);
  x -> ratio = FALSE;
  x -> labels = mk_string_array(0);
  x -> barnames = mk_string_array(0);
  return(x);
}

void free_sspart(sspart *x)
{
  if ( x->freqs != NULL ) free_dym(x->freqs);
  if ( x->labels != NULL ) free_string_array(x->labels);
  if ( x->barnames != NULL ) free_string_array(x->barnames);
  AM_FREE(x,sspart);
}

void fprintf_sspart(FILE *s,char *m1,sspart *ssp,char *m2)
{
  char *buff;

  buff = mk_printf("%s -> freqs",m1);
  fprintf_dym(s,buff,ssp->freqs,m2);
  free_string(buff);

  buff = mk_printf("%s -> ratio",m1);
  fprintf_bool(s,buff,ssp->ratio,m2);
  free_string(buff);

  buff = mk_printf("%s -> barnames",m1);
  fprintf_string_array(s,buff,ssp->barnames,m2);
  free_string(buff);

  buff = mk_printf("%s -> labels",m1);
  fprintf_string_array(s,buff,ssp->labels,m2);
  free_string(buff);
}

sspart *mk_copy_sspart(sspart *x)
{
  sspart *nx = AM_MALLOC(sspart);
  nx->freqs = (x->freqs==NULL) ? NULL : mk_copy_dym(x->freqs);
  nx->ratio = x -> ratio;
  nx->labels = (x->labels==NULL) ? NULL : mk_copy_string_array(x->labels);
  nx->barnames = (x->barnames==NULL) ? NULL : mk_copy_string_array(x->barnames);
  return(nx);
}

dym *mk_copy_dym_with_extra_column_of_zeroes(dym *x)
{
  int rows = dym_rows(x);
  int oldcols = dym_cols(x);
  int newcols = oldcols + 1;
  dym *y = mk_dym(rows,newcols);
  int i,j;
  for ( i = 0 ; i < rows ; i++ )
    for ( j = 0 ; j < oldcols ; j++ )
      dym_set(y,i,j,dym_ref(x,i,j));
  for ( i = 0 ; i < rows ; i++ )
    dym_set(y,i,oldcols,0.0);
  return y;
}

dym *mk_copy_dym_with_extra_row_of_zeroes(dym *x)
{
  int cols = dym_cols(x);
  int oldrows = dym_rows(x);
  int newrows = oldrows + 1;
  dym *y = mk_dym(newrows,cols);
  int i,j;
  for ( i = 0 ; i < oldrows ; i++ )
    for ( j = 0 ; j < cols ; j++ )
      dym_set(y,i,j,dym_ref(x,i,j));
  for ( j = 0 ; j < cols ; j++ )
    dym_set(y,oldrows,j,0.0);
  return y;
}

void sspart_maybe_expand_num_classes(sspart *ssp,int classnum)
{
  if ( classnum < 0 || classnum > 1000 )
    my_error("sspart classnums should be between 0 -- 1000");
  while ( dym_cols(ssp->freqs) <= classnum )
  {
    dym *new_freqs = mk_copy_dym_with_extra_column_of_zeroes(ssp->freqs);
    free_dym(ssp->freqs);
    ssp->freqs = new_freqs;
    add_to_string_array(ssp->labels,NULL);
  }
}

void sspart_maybe_expand_num_bars(sspart *ssp,int barnum)
{
  /*  if ( barnum < 0 || barnum > 1000 )
    my_error("sspart barnums should be between 0 -- 1000");
    */
  while ( dym_rows(ssp->freqs) <= barnum )
  {
    dym *new_freqs = mk_copy_dym_with_extra_row_of_zeroes(ssp->freqs);
    free_dym(ssp->freqs);
    ssp->freqs = new_freqs;
    add_to_string_array(ssp->barnames,NULL);
  }
}

int sspart_num_classes(sspart *ssp)
{
  return(dym_cols(ssp->freqs));
}

int sspart_num_bars(sspart *ssp)
{
  return(dym_rows(ssp->freqs));
}

void add_to_sspart(sspart *ssp,int barnum,int classnum,double count)
{
  sspart_maybe_expand_num_bars(ssp,barnum);
  sspart_maybe_expand_num_classes(ssp,classnum);
  dym_increment(ssp->freqs,barnum,classnum,(double)count);
}

void sspart_set_class_label(sspart *ssp,int classnum,char *label)
{
  sspart_maybe_expand_num_classes(ssp,classnum);
  string_array_set(ssp->labels,classnum,label);
}

void sspart_set_bar_label(sspart *ssp,int barnum,char *label)
{
  sspart_maybe_expand_num_bars(ssp,barnum);
  string_array_set(ssp->barnames,barnum,label);
}

void sspart_show_ratio(sspart *ssp)
{
  ssp -> ratio = TRUE;
}

void get_sspart_xlo_xhi(sspart *ssp,double *r_xlo,double *r_xhi)
{
  *r_xlo = -0.5;
  *r_xhi = sspart_num_bars(ssp) + 0.5;
}

agbox *mk_agbox_surrounding_sspart(sspart *ssp)
{
  double xlo;
  double xhi;
  double yhi;
  agbox *image;

  get_sspart_xlo_xhi(ssp,&xlo,&xhi);

  yhi = get_yhi_from_freqs(ssp->freqs,ssp->ratio);
  image = mk_agbox(xlo,0.0,xhi,yhi);
  return image;
}

void maybe_expand_image_from_sspart(agbox **r_image,sspart *ssp)
{
  if ( ssp != NULL )
  {
    agbox *image = mk_agbox_surrounding_sspart(ssp);
    if ( *r_image == NULL )
      *r_image = mk_copy_agbox(image);
    else
      maybe_expand_agbox_from_agbox(*r_image,image);
    free_agbox(image);
  }
}

void render_sspart(map *mp,sspart *ssp)
{
  int old_color = ag_pen_color();
  ongr_plot_hist(mp,ssp->freqs,ssp->ratio);
  ag_set_pen_color(old_color);
}

sspart *sspart_ref_maybe_create(lingraph *lg)
{
  if ( lg->ssp == NULL )
    lg -> ssp = mk_empty_sspart();
  return lg->ssp;
}

/*********** Begin visible-to-lingraph-creating-programmer
             ss-API ********/

/* Add a count to the specified part of the symbolic-symbolic
   component of the lingraph. */
/* Remember that the SS part of a lingraph is where you may 
   graph a histogram indexed by different values of a symbolic
   attribute along the X-axis, and where if you wish, the
   histogram bars may be broken up according to different
   values of the "class"...a second symbolic attribute. */
void lingraph_ss_add(lingraph *lg,int barnum,int classnum,double count)
{
  add_to_sspart(sspart_ref_maybe_create(lg),barnum,classnum,count);
}

/* Sets the text label associated with the classnum'th
   class (Y-symbolic-value) */
/* Remember that the SS part of a lingraph is where you may 
   graph a histogram indexed by different values of a symbolic
   attribute along the X-axis, and where if you wish, the
   histogram bars may be broken up according to different
   values of the "class"...a second symbolic attribute. */
void lingraph_ss_set_class_label(lingraph *lg,int classnum,char *label)
{
  sspart_set_class_label(sspart_ref_maybe_create(lg),classnum,label);
}

/* Sets the text label associated with the barnum'th
   bar (X-symbolic-value) */
/* Remember that the SS part of a lingraph is where you may 
   graph a histogram indexed by different values of a symbolic
   attribute along the X-axis, and where if you wish, the
   histogram bars may be broken up according to different
   values of the "class"...a second symbolic attribute. */
void lingraph_ss_set_bar_label(lingraph *lg,int barnum,char *label)
{
  sspart_set_bar_label(sspart_ref_maybe_create(lg),barnum,label);
}

/* If you call this, the histrogram will have all bars
   stretched to the same height so you can see the
   various ratios of y-class (classnum) values. */
/* Remember that the SS part of a lingraph is where you may 
   graph a histogram indexed by different values of a symbolic
   attribute along the X-axis, and where if you wish, the
   histogram bars may be broken up according to different
   values of the "class"...a second symbolic attribute. */
void lingraph_ss_show_ratio(lingraph *lg)
{
  sspart_show_ratio(sspart_ref_maybe_create(lg));
}

/*********** End symbolic-symbolic stuff **********/

laxis *mk_empty_laxis(void)
{
  laxis *x = AM_MALLOC(laxis);
  x -> name = NULL;
  x -> min_defined = FALSE;
  x -> min_value = -777;
  x -> max_defined = FALSE;
  x -> max_value = -777;
  return(x);
}

lingraph *mk_empty_lingraph(void)
{
  lingraph *x = AM_MALLOC(lingraph);
  x -> rsp = NULL;
  x -> ssp = NULL;
  x -> lines = mk_empty_dym_array();
  x -> color_codes = mk_ivec(0);
  x -> convert_color_code_to_ag_color = mk_ivec(0);

  x -> styles = mk_ivec(0);
  x -> labels = mk_string_array(0);
  x -> title = NULL;
  x -> string_locations = mk_dym(0,2);
  x -> strings = mk_string_array(0);

  x -> x = mk_empty_laxis();
  x -> y = mk_empty_laxis();

  return(x);
}

void free_laxis(laxis *x)
{
  if ( x->name != NULL ) free_string(x->name);
  AM_FREE(x,laxis);
}

void free_lingraph(lingraph *x)
{
  if ( x->rsp != NULL ) free_rspart(x->rsp);
  if ( x->ssp != NULL ) free_sspart(x->ssp);
  if ( x->lines != NULL ) free_dym_array(x->lines);
  if ( x->color_codes != NULL ) free_ivec(x->color_codes);
  if ( x->convert_color_code_to_ag_color != NULL ) free_ivec(x->convert_color_code_to_ag_color);
  if ( x->styles != NULL ) free_ivec(x->styles);
  if ( x->labels != NULL ) free_string_array(x->labels);
  if ( x->title != NULL ) free_string(x->title);
  if ( x->string_locations != NULL ) free_dym(x->string_locations);
  if ( x->strings != NULL ) free_string_array(x->strings);
  if ( x->x != NULL ) free_laxis(x->x);
  if ( x->y != NULL ) free_laxis(x->y);

  AM_FREE(x,lingraph);
}

laxis *mk_copy_laxis(laxis *x)
{
  laxis *nx = AM_MALLOC(laxis);
  nx->name = (x->name==NULL) ? NULL : mk_copy_string(x->name);
  nx -> min_defined = x -> min_defined;
  nx -> min_value = x -> min_value;
  nx -> max_defined = x -> max_defined;
  nx -> max_value = x -> max_value;
  return(nx);
}

lingraph *mk_copy_lingraph(lingraph *x)
{
  lingraph *nx = AM_MALLOC(lingraph);
  nx->rsp = (x->rsp==NULL) ? NULL : mk_copy_rspart(x->rsp);
  nx->ssp = (x->ssp==NULL) ? NULL : mk_copy_sspart(x->ssp);
  nx->lines = (x->lines==NULL) ? NULL : mk_copy_dym_array(x->lines);
  nx->color_codes = (x->color_codes==NULL) ? NULL : mk_copy_ivec(x->color_codes);
  nx->convert_color_code_to_ag_color = (x->convert_color_code_to_ag_color==NULL) ? NULL : mk_copy_ivec(x->convert_color_code_to_ag_color);
  nx->styles = (x->styles==NULL) ? NULL : mk_copy_ivec(x->styles);
  nx->labels = (x->labels==NULL) ? NULL : mk_copy_string_array(x->labels);
  nx->title = (x->title==NULL) ? NULL : mk_copy_string(x->title);
  nx->string_locations = (x->string_locations==NULL) ? NULL : mk_copy_dym(x->string_locations);
  nx->strings = (x->strings==NULL) ? NULL : mk_copy_string_array(x->strings);
  nx->x = (x->x==NULL) ? NULL : mk_copy_laxis(x->x);
  nx->y = (x->y==NULL) ? NULL : mk_copy_laxis(x->y);

  return(nx);
}

void fprintf_laxis(FILE *s,char *m1,laxis *x,char *m2)
{
  char buff[100];
  sprintf(buff,"%s -> name",m1);
  fprintf_string(s,buff,x->name,m2);
  sprintf(buff,"%s -> min_defined",m1);
  fprintf_bool(s,buff,x->min_defined,m2);
  sprintf(buff,"%s -> min_value",m1);
  fprintf_double(s,buff,x->min_value,m2);
  sprintf(buff,"%s -> max_defined",m1);
  fprintf_bool(s,buff,x->max_defined,m2);
  sprintf(buff,"%s -> max_value",m1);
  fprintf_double(s,buff,x->max_value,m2);
}

void fprintf_lingraph(FILE *s,char *m1,lingraph *x,char *m2)
{
  char buff[100];
  sprintf(buff,"%s -> rsp",m1);
  fprintf_rspart(s,buff,x->rsp,m2);
  sprintf(buff,"%s -> ssp",m1);
  fprintf_sspart(s,buff,x->ssp,m2);
  sprintf(buff,"%s -> lines",m1);
  fprintf_dym_array(s,buff,x->lines,m2);
  sprintf(buff,"%s -> color_codes",m1);
  fprintf_ivec(s,buff,x->color_codes,m2);
  sprintf(buff,"%s -> convert_color_code_to_ag_color",m1);
  fprintf_ivec(s,buff,x->convert_color_code_to_ag_color,m2);
  sprintf(buff,"%s -> styles",m1);
  fprintf_ivec(s,buff,x->styles,m2);
  sprintf(buff,"%s -> labels",m1);
  fprintf_string_array(s,buff,x->labels,m2);
  sprintf(buff,"%s -> title",m1);
  fprintf_string(s,buff,x->title,m2);
  sprintf(buff,"%s -> string_locations",m1);
  fprintf_dym(s,buff,x->string_locations,m2);
  sprintf(buff,"%s -> strings",m1);
  fprintf_string_array(s,buff,x->strings,m2);
  sprintf(buff,"%s -> x",m1);
  fprintf_laxis(s,buff,x->x,m2);
  sprintf(buff,"%s -> y",m1);
  fprintf_laxis(s,buff,x->y,m2);
  sprintf(buff,"%s -> line_layers",m1);
}

void plingraph(lingraph *lg)
{
  fprintf_lingraph(stdout,"lg",lg,"\n");
}

int next_color_code(ivec *color_codes)
{
  if ( ivec_size(color_codes)==0 )
    return(0);
  else
    return(1 + ivec_max(color_codes));
}

void maybe_expand_num_lines(lingraph *lg,int line_num)
{
  if ( line_num < 0 || line_num > 1000 )
    my_error("lingraph linenums should be between 0 -- 1000");
  while ( dym_array_size(lg->lines) <= line_num )
  {
    dym *new_line = mk_dym(0,2);
    int nextcolor = next_color_code(lg->color_codes);
    add_to_dym_array(lg->lines,new_line);
    add_to_ivec(lg->color_codes,nextcolor);
    add_to_ivec(lg->convert_color_code_to_ag_color,1);
    add_to_ivec(lg->styles,JOINED_STYLE);
    add_to_string_array(lg->labels,NULL);
    free_dym(new_line);
  }
}

int lingraph_num_lines(lingraph *lg)
{
  return(dym_array_size(lg->lines));
}

dym *lingraph_line_ref(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  return(dym_array_ref(lg->lines,line_num));
}

void add_to_lingraph(lingraph *lg,int line_num,double x,double y)
{
  dym *line = lingraph_line_ref(lg,line_num);
  add_row(line);
  dym_set(line,dym_rows(line)-1,0,x);
  dym_set(line,dym_rows(line)-1,1,y);
}

void set_lines_same_color(lingraph *lg,int line_num1,int line_num2)
{
  maybe_expand_num_lines(lg,line_num1);
  maybe_expand_num_lines(lg,line_num2);
  if ( line_num1 != line_num2 )
  {
    int col1 = ivec_ref(lg->color_codes,line_num1);
    int col2 = ivec_ref(lg->color_codes,line_num2);
    if ( col1 != col2 )
    {
      int i;
      ivec_set(lg->color_codes,line_num2,col1);
      if ( !is_in_ivec(lg->color_codes,col2) )
      {
        int new_max_col = next_color_code(lg->color_codes)-1;
        if ( new_max_col > col2 )
          for ( i = 0 ; i < ivec_size(lg->color_codes) ; i++ )
            if ( ivec_ref(lg->color_codes,i) == new_max_col )
              ivec_set(lg->color_codes,i,col2);
      }
    }
    col1 = ivec_ref(lg->convert_color_code_to_ag_color,line_num1);
    col2 = ivec_ref(lg->convert_color_code_to_ag_color,line_num2);
    if( col1 != col2 )
    {
      ivec_set(lg->convert_color_code_to_ag_color,line_num2,col1);
    }
  }
}
/* -- Added by Artur --- Mar, 2000 ---------- */
void set_line_color(lingraph *lg, int line_num, int color )
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->color_codes,line_num,color);
  unset_line_convert_color_code_to_ag_color( lg, line_num );
}
bool is_line_convert_color_code_to_ag_color_set( lingraph *lg, int line_num )
{
  maybe_expand_num_lines(lg,line_num);
  return( ivec_ref(lg->convert_color_code_to_ag_color,line_num) );
}
void set_line_convert_color_code_to_ag_color( lingraph *lg, int line_num )
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->convert_color_code_to_ag_color,line_num,1);
}
void unset_line_convert_color_code_to_ag_color( lingraph *lg, int line_num )
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->convert_color_code_to_ag_color,line_num,0);
}
void set_line_style_dotted(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,DOTTED_STYLE);
}
void set_line_style_pixel(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,PIXEL_STYLE);
}
void set_line_style_cross(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,CROSS_STYLE);
}
void set_line_style_smallbox(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,SMALLBOX_STYLE);
}
void set_line_style_smalltriangle(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,SMALLTRIANGLE_STYLE);
}
void set_line_style_smallcircle(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,SMALLCIRCLE_STYLE);
}
void set_line_style_fancy(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,FANCY_STYLE);
}
void set_line_style_disc(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,DISC_STYLE);
}
void set_line_style_smallplus(lingraph *lg,int line_num)
{
  maybe_expand_num_lines(lg,line_num);
  ivec_set(lg->styles,line_num,SMALLPLUS_STYLE);
}
void maybe_replace(char **x,char *label)
{
  if ( *x != NULL ) free_string(*x);
  *x = mk_copy_string(label);
}

void set_x_axis_label(lingraph *lg,char *label)
{
  maybe_replace(&(lg->x->name),label);
}

void set_y_axis_label(lingraph *lg,char *label)
{
  maybe_replace(&(lg->y->name),label);
}

void set_lingraph_title(lingraph *lg,char *label)
{
  maybe_replace(&(lg->title),label);
}

void add_string_to_lingraph(lingraph *lg,double x,double y,char *label)
{
  add_row(lg->string_locations);
  dym_set(lg->string_locations,dym_rows(lg->string_locations)-1,0,x);
  dym_set(lg->string_locations,dym_rows(lg->string_locations)-1,1,y);
  add_to_string_array(lg->strings,label);
}

void set_line_label(lingraph *lg,int line_num,char *label)
{
  maybe_expand_num_lines(lg,line_num);
  string_array_set(lg->labels,line_num,label);
}

void set_x_min(lingraph *lg,double xmin)
{
  lg->x->min_defined = TRUE;
  lg->x->min_value = xmin;
}

void set_x_max(lingraph *lg,double xmax)
{
  lg->x->max_defined = TRUE;
  lg->x->max_value = xmax;
}

void set_y_min(lingraph *lg,double ymin)
{
  lg->y->min_defined = TRUE;
  lg->y->min_value = ymin;
}

void set_y_max(lingraph *lg,double ymax)
{
  lg->y->max_defined = TRUE;
  lg->y->max_value = ymax;
}
void maybe_reset_x_min(lingraph *lg,double xmin)
{
  if( lg->x->min_defined )
    lg->x->min_value = real_min(xmin,lg->x->min_value);
  else
  {
    lg->x->min_defined = TRUE;
    lg->x->min_value = xmin;
  }
}

void maybe_reset_x_max(lingraph *lg,double xmax)
{
  if( lg->x->max_defined )
    lg->x->max_value = real_max(xmax,lg->x->max_value);
  else
  {
    lg->x->max_defined = TRUE;
    lg->x->max_value = xmax;
  }
}

void maybe_reset_y_min(lingraph *lg,double ymin)
{
  if( lg->y->min_defined )
    lg->y->min_value = real_min(ymin,lg->y->min_value);
  else
  {
    lg->y->min_defined = TRUE;
    lg->y->min_value = ymin;
  }
}

void maybe_reset_y_max(lingraph *lg,double ymax)
{
  if( lg->y->max_defined )
    lg->y->max_value = real_max(ymax,lg->y->max_value);
  else
  {
    lg->y->max_defined = TRUE;
    lg->y->max_value = ymax;
  }
}

dyv *mk_xarr_from_lingraph(lingraph *lg,int line_num)
{
  dym *line = lingraph_line_ref(lg,line_num);
  return mk_dyv_from_dym_col(line,0);
}

dyv *mk_yarr_from_lingraph(lingraph *lg,int line_num)
{
  dym *line = lingraph_line_ref(lg,line_num);
  return mk_dyv_from_dym_col(line,1);
}

int make_xarr_yarr_from_lingraph(lingraph *lg,int line_num,
				 double **xarr,double **yarr)
{
  dym *line = lingraph_line_ref(lg,line_num);
  int size = dym_rows(line);
  int i;

  *xarr = AM_MALLOC_ARRAY(double,size);
  *yarr = AM_MALLOC_ARRAY(double,size);

  for ( i = 0 ; i < size ; i++ )
  {
    (*xarr)[i] = dym_ref(line,i,0);
    (*yarr)[i] = dym_ref(line,i,1);
  }

  return(size);
}

void maybe_expand_image_from_laxis(agbox *agb,laxis *lx,int comp)
{
  if ( lx -> min_defined )
    maybe_expand_agbox_comp(agb,comp,lx->min_value);
  if ( lx -> max_defined )
    maybe_expand_agbox_comp(agb,comp,lx->max_value);
}

static void lingraph_render_line_in_map( lingraph *lg, int line_number, map *mp )
{
  dyv *xarr = mk_xarr_from_lingraph(lg,line_number);
  dyv *yarr = mk_yarr_from_lingraph(lg,line_number);
  int z = ivec_ref(lg->color_codes,line_number);
  int amut_col = (is_line_convert_color_code_to_ag_color_set( lg, line_number )) ? color_code_to_ag_color(z) : z; 
  bool join = ivec_ref(lg->styles,line_number)==JOINED_STYLE;
  int marktype;
  if(join) 
    marktype = -1;
  else
  {
    switch( (ivec_ref(lg->styles,line_number)) )
    {
    case DOTTED_STYLE: marktype = DOT_MARKTYPE; break;
    case CROSS_STYLE: marktype = CROSS_MARKTYPE; break;
    case SMALLBOX_STYLE: marktype = SMALLBOX_MARKTYPE; break;
    case SMALLTRIANGLE_STYLE: marktype = SMALLTRIANGLE_MARKTYPE; break;
    case SMALLCIRCLE_STYLE: marktype = SMALLCIRCLE_MARKTYPE; break;
    case PIXEL_STYLE: marktype = PIXEL_MARKTYPE; break;
    case FANCY_STYLE: marktype = FANCY_MARKTYPE; break;
    case DISC_STYLE: marktype = DISC_MARKTYPE; break;
    case SMALLPLUS_STYLE: marktype = SMALLPLUS_MARKTYPE; break;
    default: marktype = PIXEL_MARKTYPE; break;
    }
  }
  plot_graphic_in_frame(mp,xarr,yarr,join,marktype,amut_col);      
  free_dyv(xarr);
  free_dyv(yarr);
}

void render_lingraph_in_agbox(lingraph *lg,agbox *ag_frame)
{
  map *mp = NULL;
  int i;
  agbox *real_agb = mk_copy_agbox(ag_frame);
  agbox *image = NULL;
  int num_lines = lingraph_num_lines(lg);
  ag_set_pen_color(AG_BLACK);
  maybe_print_and_shrink(real_agb,lg->title);

  if ( num_lines > 1 )
  {
    ivec *actual_colors = mk_copy_ivec( lg->color_codes );
    for(i=0;i<num_lines;i++)
    {
      if( is_line_convert_color_code_to_ag_color_set( lg, i )) {
        ivec_set( actual_colors, i, color_code_to_ag_color(ivec_ref(actual_colors,i)));
      }
    }
    print_color_key_and_shrink_special_colors(real_agb,lg->labels,actual_colors);
    free_ivec(actual_colors);
  }
  if ( lg->rsp != NULL )
    print_color_key_and_shrink(real_agb,lg->rsp->labels);

  if ( lg->ssp != NULL )
    print_color_key_and_shrink(real_agb,lg->ssp->labels);

  for ( i = 0 ; i < num_lines ; i++ )
  {
    dyv *xarr = mk_xarr_from_lingraph(lg,i);
    dyv *yarr = mk_yarr_from_lingraph(lg,i);

    if ( dyv_size(xarr) > 0 )
    {
      if ( image == NULL )
        image = mk_agbox_surrounding_dyvs(xarr,yarr);
      else
        maybe_expand_agbox_from_dyvs(image,xarr,yarr);
    }

    free_dyv(xarr);
    free_dyv(yarr);

    /* printf("line %d, color: %d\n",i,ivec_reg(lg->color_codes)); */
  }

  if ( image == NULL )
  {
    if ( lg->x->min_defined && lg->y->min_defined &&
         lg->x->max_defined && lg->y->max_defined )
      image = mk_agbox(lg->x->min_value,lg->y->min_value,
                       lg->x->max_value,lg->y->max_value);
  }

  if( lg->rsp != NULL )
    maybe_expand_image_from_rspart(&image,lg->rsp);
  if( lg->ssp != NULL )
    maybe_expand_image_from_sspart(&image,lg->ssp);


  if( image==NULL )
    printf("render_lingraph_in_agbox(): nothing to render.\n");
  else
  {    
    maybe_expand_image_from_laxis(image,lg->x,XCOMP);
    maybe_expand_image_from_laxis(image,lg->y,YCOMP);

    mp = mk_map_from_frame_and_image(real_agb,image);
    
    maybe_draw_map_label(mp,XCOMP,lg->x->name);
    maybe_draw_map_label(mp,YCOMP,lg->y->name);
    /* printf(" completed\n");fflush(stdout); */
    /* printf("That took %d sec(s)\n",global_time()-start_secs); */
    
    if ( lg->rsp != NULL )
      render_rspart(mp,lg->rsp);
    
    if ( lg->ssp != NULL )
      render_sspart(mp,lg->ssp);

    for ( i = 0 ; i < num_lines ; i++ )
      lingraph_render_line_in_map(lg,i,mp);
    
    if ( lg->ssp == NULL )
      draw_map_real_axis(mp,XCOMP);
    else
      draw_map_hist_axis(mp,XCOMP,lg->ssp->barnames);
    
    draw_map_real_axis(mp,YCOMP);
    
    ag_set_pen_color(AG_BLACK);
    
    for ( i = 0 ; i < dym_rows(lg->string_locations) ; i++ )
    {
      char *string = string_array_ref(lg->strings,i);
      double px = dym_ref(lg->string_locations,i,0);
      double py = dym_ref(lg->string_locations,i,1);
      
      ongr_print(mp,px,py,string);
    }
    
    
    free_agbox(image);
    free_map(mp);
  }
  free_agbox(real_agb);
}

void render_lingraph(lingraph *lg)
{
  agbox *agb = mk_agbox(0.0,0.0,512.0,512.0);
  render_lingraph_in_agbox(lg,agb);
  free_agbox(agb);
}

/* The set of points corresponding to K std devs from the mean is
     S = { r : (r - m)^T COV^-1 (r - m) = K^2 / 2 }

   Write COV = L L^T where L is lower triangular and write KK = K^2 / 2

     S = { r : (r - m)^T (L L^T)^-1 (r - m) = KK }
       = { r : (r - m) (L^T)^-1 L^-1 (r - m) = KK }
       = { r : [L^-1 (r - m)]^T  L^-1 (r - m) = KK }
       = { r : | L^-1 (r - m)] | = sqrt(KK) }
       = { r : exists t with L^-1 (r - m) = sqrt(KK) * (cos t, sin t) }
       = { r : exists t with r = m + sqrt(KK) * L * (cos t, sin t) }
*/
void add_ellipse_to_lingraph(lingraph *lg,int line,dyv *mean,dym *cov,double k_std_devs)
{
  dym *l = mk_dym(dym_rows(cov),dym_cols(cov));
  bool ok = attempt_cholesky_decomp(cov,l);
  int i;
  int steps = 32;

  if ( !ok )
  {
    printf("Big problem. The following covariance matrix isn't sym. pos. def:\n");
    fprintf_dym(stdout,"cov",cov,"\n");
  }

  for ( i = 0 ; ok && i <= steps ; i++ )
  {
    double t1 = i * 2.0 * PI / (double) steps;
    dyv *unit1 = mk_dyv_2(cos(t1),sin(t1));
    dyv *r1 = mk_dym_times_dyv(l,unit1);
    dyv_scalar_mult(r1,k_std_devs/sqrt(2.0),r1);
    dyv_plus(r1,mean,r1);
    add_to_lingraph(lg,line,dyv_ref(r1,0),dyv_ref(r1,1));
    free_dyv(unit1);
    free_dyv(r1);
  }

  free_dym(l);
}    

/*------------------------------------------
  This adds an ellipse 'linenum' to lingraph 'lg'
  ellipse is defined by its center coordinates 'x0' 'y0',
  half-axes lengths 'a' and 'b' and rotation angle 'phi' 
  (phi in degrees!!!)
  'resolution' determines how many line segments will
  compose the ellipse. Put resolution=72 to get one line
  segment per a 5 degree angle slice (360/72 = 5).
  Coded by Artur Dubrawski, 03 Aug. 1996
------------------------------------------*/
void add_parametric_ellipse_to_lingraph(lingraph *lg, int linenum, 
			                                  double x0, double y0, double a, double b, double phi, 
			                                  int resolution )
{
  double step = 2*PI/(double)resolution;
  double t = 0.0;
  int i;
  double sinphi = sin(phi*PI/180.0);
  double cosphi = cos(phi*PI/180.0);
  double x,y;

  for( i=0; i<=resolution; i++ )
    {
      x = a*cos(t);
      y = b*sin(t);
      add_to_lingraph( lg, linenum, x0+x*cosphi-y*sinphi, y0+x*sinphi+y*cosphi );
      t += step;
    }

  return;
}

void add_vertical_downward_arrow_to_rs_lingraph(lingraph *lg,double x, int color)
{
  int max = lingraph_rs_max_bar_height(lg);
  double arrow_width = lingraph_rs_max_bar_width(lg) * 0.05;
  int line = lingraph_num_lines(lg);

  add_to_lingraph(lg,line,x,max);
  add_to_lingraph(lg,line,x,0);
  add_to_lingraph(lg,line,x+arrow_width,max*0.05);
  add_to_lingraph(lg,line,x,0);
  add_to_lingraph(lg,line,x-arrow_width,max*0.05);

  set_line_color(lg, line, color);

  /* printf("adding %d with max = %d\n", line, max); */
}

void add_dyv_to_lingraph(lingraph *lg,int line,dyv *dv)
{
  int i;
  for ( i = 0 ; i < dyv_size(dv) ; i++ )
    add_to_lingraph(lg,line,(double)i,dyv_ref(dv,i));
}

lingraph *mk_lingraph_from_dyv(char *title,dyv *dv)
{
  lingraph *lg = mk_empty_lingraph();
  set_lingraph_title(lg,title);
  add_dyv_to_lingraph(lg,0,dv);
  return lg;
}

void lingraph_draw_dyv(char *title,dyv *dv)
{
  lingraph *lg = mk_lingraph_from_dyv(title,dv);
  render_lingraph(lg);
  free_lingraph(lg);
}

/*
 This is auxiliary to the mk_color_imat() implemented below.
 It returns an index of bin of x to which val belongs.
 ivec indices contains indices of sorted x.

 if val < dyv_min(x) ==  dyv_ref(x,ivec_ref(indices,0))  
    then -1 is returned
 if dyv_min(x) <= val < dyv_ref(x,ivec_ref(indices,1))
    then ivec_ref(indices,0) is returned
 if dyv_ref(x,ivec_ref(indices,1)) <= val < dyv_ref(x,ivec_ref(indices,2))
    then ivec_ref(indices,1) is returned
 ...
 if dyv_ref(x,ivec_ref(indices,size-2)) <= val <= dyv_max(x)
    then ivec_ref(indices,size-1) is returned
 if val > dyv_max(x) then -1 is returned 

 [size==dyv_size(x)==ivec_size(indices)]

 search for bin runs the bisection algorithm
*/
int bin_index( double val, dyv *x, ivec *indices )
{
  if( val > dyv_max(x) || val < dyv_min(x) )
    return(-1);
  else if( dyv_max(x)==dyv_min(x) )
    return(0);
  else
  {
    int size = dyv_size( x );
    int index = size/2;
    if( size > 1 )
    {
      bool done = FALSE;
      double hi, lo;
      int shift;
      while ( !done )
      {
        lo = dyv_ref( x, ivec_ref(indices,index-1) );
        hi = dyv_ref( x, ivec_ref(indices,index) );
      
        if( size > 1 )
          size /= 2;
        shift = (size+1)/2;
        if( val <= hi )
        {
          if( val > lo || index==1 )
            done = TRUE;
          else
            index -= shift;
        }
        else
          index += shift;
      }
    }
    index = ivec_ref(indices,index-1);
    return(index);
  }
}
/*
  Precondition: 
       dyv_size(tics)==ivec_size(ag_colors)+1
*/
imat *mk_color_imat( dym *heights, dyv *tics, ivec *ag_colors )
{
  int i,j;
  int bin;
  int xsize = dym_cols(heights);
  int ysize = dym_rows(heights);
  imat *m  = mk_imat(ysize,xsize); /* mk_constant_imat(ysize,xsize,-1); */
  ivec *indices = mk_indices_of_sorted_dyv(tics);
  double val;
  if( dym_max(heights) > dyv_max(tics) )
  {
    printf("Warning: dym_max(heights)==%g > dyv_max(tics)==%g in mk_color_imat() (the difference==%g) !!!\n"
           "         This would cause negative entries in the generated imat.\n",
           dym_max(heights),dyv_max(tics),dym_max(heights)-dyv_max(tics));
  }
  if( dym_min(heights) < dyv_min(tics) )
  {
    printf("Warning: dym_min(heights)==%g < dyv_min(tics)==%g in mk_color_imat() (the difference==%g) !!!\n"
           "         This would cause negative entries in the generated imat.\n",
           dym_min(heights),dyv_min(tics),dyv_min(tics)-dym_min(heights));
  }
  for(i=0;i<xsize;i++)
  {
    for(j=0;j<ysize;j++)
    {
      val = dym_ref(heights,j,i);
      bin = bin_index( val, tics, indices );
      imat_set( m, j, i, (bin<0?-1:ivec_ref(ag_colors,bin)));
    }
  }
  free_ivec(indices);
  return(m);
}
/*
 This one adds one more color to the imat in cases
 when dym_max(heights) > dyv_max(thresh);
 otherwise this is just a copy of the function above.
*/
imat *mk_color_imat_safe( dym *heights, dyv *tics, ivec *ag_colors )
{
  double val = dym_max( heights );
  imat *m = NULL;
  if( val > dyv_max(tics) )
  {
    dyv *t = mk_copy_dyv(tics);
    ivec *c = mk_copy_ivec(ag_colors);
    int size = ivec_size(ag_colors);
    int col = color_code_to_ag_color( ivec_ref(ag_colors,size-1) + 1 );
    if( size < NUM_AG_COLORS )
    {
      while( is_in_ivec(ag_colors,col) )
        col = color_code_to_ag_color( col + 1 );
    }
    add_to_dyv(t,val);
    add_to_ivec(c,col);
    m = mk_color_imat( heights, t, c );
    free_ivec(c);
    free_dyv(t);
  }
  else
    m = mk_color_imat( heights, tics, ag_colors );
  return(m);
}


void draw_lingraph(lingraph *lg,char *file)
{
  ag_on((file)? file:"");
  render_lingraph(lg);
  ag_off();
}

lingraph *mk_histogram(dyv *x,char *xname)
{
  lingraph *lg = mk_empty_lingraph();
  int i;
  for ( i = 0 ; i < dyv_size(x) ; i++ )
    lingraph_rs_add(lg,dyv_ref(x,i),1.0,0);
  set_x_axis_label(lg,xname);
  return lg;
}

void render_histogram(dyv *x,char *xname)
{
  lingraph *lg = mk_histogram(x,xname);
  render_lingraph(lg);
  free_lingraph(lg);
}

lingraph *mk_lingraph_from_dyvs(dyv *x,char *xname,dyv *y,char *yname)
{
  lingraph *lg = mk_empty_lingraph();
  int i;
  my_assert(dyv_size(x) == dyv_size(y));
  for ( i = 0 ; i < dyv_size(x) ; i++ )
    add_to_lingraph(lg,0,dyv_ref(x,i),dyv_ref(y,i));

  if (dyv_size(x) < 100)       set_line_style_disc(lg,0);
  else if (dyv_size(x) < 1000) set_line_style_dotted(lg,0);
  else                         set_line_style_pixel(lg,0);

  set_x_axis_label(lg,xname);
  set_y_axis_label(lg,yname);

  return lg;
}

void render_dyvs(dyv *x,char *xname,dyv *y,char *yname)
{
  lingraph *lg = mk_lingraph_from_dyvs(x,xname,y,yname);
  render_lingraph(lg);
  free_lingraph(lg);
}

