/*
   File:        awdut.c
   Author:      Artur Dubrawski
   Created:     July 1999
   Description: AWD's general purpose utilities

   Copyright 1999, Schenley Park Research Inc
*/

#include "awdut.h"

dyv *mk_default_tics_dyv( double xmin, double xmax, int resolution )
{
  dyv *t = NULL;
  if( resolution < 1 )
    my_error("mk_default_tics_dyv: illegal resolution");
  else if( xmin > xmax )
    my_error("mk_default_tics_dyv: xmin > xmax");
  else
  {
    int resol = (xmin==xmax?1:resolution); /* precaution against xmax==xmin */
    double x = xmin;
    double dx = (xmax-xmin)/resol;
    int i;
    t = mk_dyv(resol+1);
    for(i=0;i<resol;i++)
    {
      dyv_set(t,i,x);
      x += dx;
    }
    dyv_set(t,resol,xmax);
  }
  return(t);
}

int classical_number_intersects_from_lines(dyv *xb, dyv *yb, dyv *xe, dyv *ye)
{
  int nl = dyv_size(xb);
  int i, j;
  int ni = 0;
  dyv *slope = NULL;
  dyv *yinter = NULL;

  if(nl != dyv_size(yb) || dyv_size(yb) != dyv_size(xe) || dyv_size(xe) != dyv_size(ye))
  {
    my_warning("inputs to classical_number_intersects_from_lines does not have the same size");
    return -1;
  }

  if(nl == 1)
  {
    return 0;
  }

  slope = mk_dyv(nl);
  yinter = mk_dyv(nl);

  /*preproc the slope and y-intercept*/
  for(i = 0; i < nl; i++)
  {
    double x1 = dyv_ref(xb,i);
    double x2 = dyv_ref(xe,i);
    double y1 = dyv_ref(yb,i);
    double y2 = dyv_ref(ye,i);

    double m = (x1 != x2) ? (y2 - y1) / (x2 - x1) : HUGE_VAL;
    double c = (x1 != x2) ? y1 - m * x1 : HUGE_VAL;

    dyv_set(slope, i, m);
    dyv_set(yinter, i, c);
  }

  /*calculating intercepts now.*/
  for(i = 0; i < nl - 1; i++)
  {
    /* slope, y-intercept, and x-coords of the ith line */
    double m1 = dyv_ref(slope, i);
    double c1 = dyv_ref(yinter, i);
    double x1b = dyv_ref(xb,i);
    double x1e = dyv_ref(xe,i);

    for(j = i + 1; j < nl; j++)
    {
      /* slope, y-intercept, and x-coords of the jth line */
      double m2 = dyv_ref(slope, j);
      double c2 = dyv_ref(yinter, j);
      double x2b = dyv_ref(xb,j);
      double x2e = dyv_ref(xe,j);

      /* if they have same slope, skip */
      if(m1 == m2)
        continue;

      /* if they have same end points, skip */
      if(x1b == x2b || x1b == x2e || x1e == x2b || x1e == x2e)
        continue;

      /* if the first line is vertical and 2nd is not */
      if(m1 == HUGE_VAL && c1 == HUGE_VAL)
      {
        double xcross = dyv_ref(xb,i);

        if((x2b > xcross && x2e < xcross) || (x2b < xcross && x2e > xcross))
          ni++;
      }
      /* if 2nd line is vertical and 1st is not */
      else if(m2 == HUGE_VAL && c2 == HUGE_VAL)
      {
        double xcross = dyv_ref(xb,j);

        if((x1b > xcross && x1e < xcross) || (x1b < xcross && x1e > xcross))
          ni++;
      }
      else
      {
	/* if xcross is within bounds of both lines, then it's an intercept */
        double xcross = (c2 - c1) / (m1 - m2);

        if( ((x1b > xcross  && x1e  < xcross) ||
	     (x1b  < xcross && x1e > xcross  )) &&
	    ((x2b > xcross  && x2e  < xcross) ||
	     (x2b  < xcross && x2e > xcross  )))       
          ni++;
      }
    }
  }

  free_dyv(slope);
  free_dyv(yinter);

  return ni;
}

/* ------- produces the last digit of an integer --------- */
int int_last_digit( int num )
{
  int l = abs(num);
  l -= ( (l/10)*10 );
  return(l);
}
/* ------- makes a two-character suffix for the ordinary number 
   ------- text representation 
   ------- (e.g. if num=2 or num=102 the result would be "nd")
   ------- use free_string() to free the allocated memory after use! */
char *mk_int_last_digit_suffix( int num )
{
  char *t = NULL;
  int l = int_last_digit( num );
  switch( l )
  {
  case 1: 
    t = mk_printf("st");
    break;
  case 2: 
    t = mk_printf("nd");
    break;
  case 3: 
    t = mk_printf("rd");
    break;
  default: 
    t = mk_printf("th");
    break;
  }
  return(t);
}

/*
  x contains some distribution -> the created dyv is its histogram
 
  nbins determines horizontal (x-wise) resolution of the histogram
  bins stores values for the subsequent bin-center locations along x

  dyv *bins is created inside and must be freed outside!!!

  the result and bins have nbins+2 entries to account for the
  pre- and post- domain range zero-count bins and make the
  drawn histogram look nicer.
  if your purpose is not that of drawing a histogram, then just
  ignore the first and the last components of the generated dyvs
  ---------------------------------------------------
*/
dyv *mk_hist_dyv_from_dyv(dyv *x,int nbins,dyv **bins)
{
  dyv *h = mk_zero_dyv(nbins+2);
  if( nbins > 0 )
  {
    double min = dyv_min(x);
    int i,bin;
    double aux,max = dyv_max(x);
    double invrange = max-min;
    double step = invrange/nbins;
    invrange = 1.0/invrange;
    /* if( *bins != NULL ) */
    /*  free_dyv(bins); */
    *bins = mk_dyv(nbins+2);
    for(i=1;i<=nbins;i++)
    {
      dyv_set(*bins,i,(min+(i-0.5)*step));
    }
    dyv_set(*bins,0,min-0.5*step);
    dyv_set(*bins,nbins+1,max+0.5*step);
    for(i=0;i<dyv_size(x);i++)
    {
      aux = (dyv_ref(x,i)-min)*invrange;
      bin = (aux<1.0 ? ((int)floor(nbins*aux))+1 : nbins);
      dyv_set(h,bin,dyv_ref(h,bin)+1.0);
    }
    dyv_set(h,0,0.0);
    dyv_set(h,nbins+1,0.0);
#if 0
    /* smoothing */
    {
      dyv *sm = mk_dyv(nbins);
      dyv_set(sm,0,(dyv_ref(h,0)+0.5*dyv_ref(h,1))/1.5);
      dyv_set(sm,nbins-1,(dyv_ref(h,nbins-1)+0.5*dyv_ref(h,nbins-2))/1.5);
      for(i=1;i<nbins-1;i++)
      {
        dyv_set(sm,i,0.5*(dyv_ref(h,i)+0.5*(dyv_ref(h,i-1)+dyv_ref(h,i+1))));
      }
      copy_dyv(sm,h);
      free_dyv(sm);
    }
#endif
    /*  normalization wrt the largest count */
#if 0
    bin = 0; /* max ind */
    max = dyv_ref(h,0);
    for(i=1;i<nbins+2;i++)
    {
      if( dyv_ref(h,i) > max )
      {
        max = dyv_ref(h,i);
        bin = i;
      }
    }
    for(i=0;i<nbins+2;i++)
    {
      dyv_set(h,i,dyv_ref(h,i)/max);
    }
#endif

  }

  return(h);
}

/* ------------------
   makes a stripped copy of the source ivec
   the new ivec contains all but the specified entry
   (if it is there in the source) */
ivec *mk_stripped_copy_ivec( ivec *src, int removeme )
{
  ivec *iv = NULL;
  if( src != NULL )
  {
    int i,v;
    iv = mk_ivec(0);
    for(i=0;i<ivec_size(src);i++)
    {
      v = ivec_ref(src,i);
      if( v != removeme )
        add_to_ivec( iv, v );      
    }
  }
  return(iv);
}
