#include "tree.h"
#include "dist.h"
#include "k_est.h"
#include <stdlib.h>

#define min(a,b) (((a)<(b))?(a):(b))

double k_noedge(               /* Calculates K(h) without edge correction */
		coord *points,       /* An array of positions */
		int n,               /* Number of points */
		double a,            /* Length of box in x-direction */
		double b,            /* Length of box in y-direction */
		double h)            /* Parameter of k */
{
  int i,j;
  double estimate=0;      /* Current estimate of k */
  double dist;       /* Euclidean distance between two points */

  if(points == NULL)
    return 0.0;

  for(i=n; i>0; --i){
    for(j=n; j>0; --j){
      if(i!=j){
	dist=edist(points+i-1,points+j-1);
	if(dist<=h){
	  estimate+=1.0/n;
	}
      }
    }
  }

  return (a*b/(n-1))*estimate;      /* return \hat{\lambda}^{-1}\times est */
}

double k_tor(              /* Calculates K(h) on a torus */
	     coord *points,       /* An array of positions */
	     int n,               /* Number of points */
	     double a,            /* Length of box in x-direction */
	     double b,            /* Length of box in y-direction */
	     double h)            /* Parameter of k */
{
  int i,j;
  double estimate=0;      /* Current estimate of k */
  double dist;       /* Euclidean distance between two points */

  if(points == NULL)
    return 0.0;

  for(i=n; i>0; --i){
    for(j=n; j>0; --j){
      if(i!=j){
	dist=tdist(points+i-1,points+j-1,a,b);
	if(dist<=h){
	  estimate+=1.0/n;
	}
      }
    }
  }

  return (a*b/(n-1))*estimate;      /* return \hat{\lambda}^{-1}\times est */
}

double k_edge(               /* Calculates K(h) with edge correction */
	      coord *points,       /* An array of positions */
	      int n,               /* Number of points */
	      double a,            /* Length of box in x-direction */
	      double b,            /* Length of box in y-direction */
	      double h)            /* Parameter of k */
{
  int i,j;
  double est=0;      /* Current estimate of k */
  double dist;       /* Euclidean distance between two points */

  if(points == NULL)
    return 0.0;

  for(i=n; i>0; --i){
    for(j=n; j>0; --j){
      if(i!=j){
	dist=edist(points+i-1,points+j-1);
	if((dist<=h) && (edge(points+j-1,a,b)>h))
	  est+=1.0/n;
      }
    }
  }
  j=0;
  for(i=n; i>0; --i){
    if(edge(points+i-1,a,b)>h)
      ++j;
  }

  if(est!=0)
    return (a*b/(n-1))*est/j;      /* return \hat{\lambda}^{-1}\times est/j */
  else
    return 0;
}

double k_rip(               /* Calculates Ripley's edge-correcting estimate 
			       for K(h) */
	     coord *points,       /* An array of positions */
	     int n,               /* Number of points */
	     double a,            /* Length of box in x-direction */
	     double b,            /* Length of box in y-direction */
	     double h)            /* Parameter of k */
{
  int i,j;
  double est=0;      /* Current estimate of k */
  double dist;       /* Euclidean distance between two points */
  double w;          /* Weight */

  if(points == NULL)
    return 0.0;

  for(i=n; i>0; --i){
    for(j=n; j>0; --j){
      if(i!=j){
	dist=edist(points+i-1,points+j-1);
	w=weight(points+i-1,dist,a,b);
	if(dist<=h && w!=0)
	  est+=1/(w*n);
      }
    }
  }

  return (a*b/(n-1))*est;      /* return \hat{\lambda}^{-1}\times est */
}

double edge(coord *point,     /* Location of point */
	    double a,       /* size of rectangle in x-direction */
	    double b)       /* size of rectangle in y-direction */
{
  double x_min;
  double y_min;

  x_min=min(point->x,a-point->x);
  y_min=min(point->y,b-point->y);

  return min(x_min,y_min);
}

double weight(coord *point,   /* Location of point */
	      double r,       /* Radius of circle */
	      double a,       /* size of rectangle in x-direction */
	      double b)       /* size of rectangle in y-direction */
{
  double theta;
  double pi=acos(-1);
  static coord bl={0.0,0.0};
  static coord tl={0.0,0.0};
  static coord br={0.0,0.0};
  static coord tr={0.0,0.0};

  if(tl.y!=b || br.x!=a){
    tl.y=tr.y=b;
    br.x=tr.x=a;
  }

  if(point->x + r > a){
    if(edist(&br,point)<r){
      if(edist(&tr,point)<r){
	if(edist(&tl,point)<r){
	  if(edist(&bl,point)<r){  /* Circle totally outside box */
	    return 0.0;
	  }
	  else{                            /* Outside all but bl */
	    theta = acos(point->x/r) + acos(point->y/r) + 3*pi/2;
	    return 1-theta/(2*pi);
	  }
	}
	else{                       /* Not outside tl but outside br & tr */
	  if(edist(&bl,point)<r){          /* Outside all but tl */
	    theta = acos(point->x/r) + acos((b-point->y)/r) + 3*pi/2;
	    return 1-theta/(2*pi);
	  }
	  else{                      /* Outside br & tr but not bl or tl */
	    theta=acos(point->y/r) + acos((b-point->y)/r) + pi;
	    if(point->x-r < 0.0){       /* Outside some of line x=0 */
	      theta+=2*acos(point->x/r);
	    }
	    return 1-theta/(2*pi);
	  }
	}
      }
      else{                       /* Not outside tr but outside br */
	if(edist(&tl,point)<r){      /* Outside br & tl but not tr */
	  if(edist(&bl,point)<r){  /* Outside all but tr */
	    theta = acos((a-point->x)/r) + acos((b-point->y)/r) + 3*pi/2;
	    return 1-theta/(2*pi);
	  }
	  else{                   /* Outside br & tl but not bl or tr 
				     This is not possible so error message */
	    fprintf(stderr,"Error in K estimation.  Something impossible ");
	    fprintf(stderr,"Just happened!\n");
	    exit(4);
	  }
	}
	else{                    /* Outside br but not tr or tl */
	  if(edist(&bl,point)<r){  /* Outside br & bl but not tr or tl */
	    theta=acos(point->x/r) + acos((a-point->x)/r) + pi;
	    if(point->y+r > b){       /* Outside some of line y=b */
	      theta+=2*acos((b-point->y)/r);
	    }
	    return 1-theta/(2*pi);
	  }
	  else{                               /* Outside br only */
	    theta=acos(point->y/r) + acos((a-point->x)/r) + pi/2;
	    if(point->y+r > b){       /* Outside some of line y=b */
	      theta+=2*acos((b-point->y)/r);
	    }
	    if(point->x-r < 0.0){       /* Outside some of line x=0 */
	      theta+=2*acos(point->x/r);
	    }
	    return 1-theta/(2*pi);
	  }
	}
      }
    }
    else{                           /* Not outside br */
      if(edist(&tr,point)<r){       /* Outside tr but not br */
	if(edist(&tl,point)<r){     /* Outside tr & tl but not br */
	  if(edist(&bl,point)<r){   /* Outside all but br */
	    theta = acos((a-point->x)/r) + acos(point->y/r) + 3*pi/2;
	    return 1-theta/(2*pi);
	  }
	  else{                    /* Outside tr & tl but not bl or br */
	    theta=acos(point->x/r) + acos((a-point->x)/r) + pi;
	    if(point->y-r < 0.0){       /* Outside some of line y=0 */
	      theta+=2*acos(point->y/r);
	    }
	    return 1-theta/(2*pi);
	  }
	}
	else{                      /* Outside tr but not br or tl */
	  if(edist(&bl,point)<r){   /* Outside tr and bl but not br or tl
				       This is not possible so error message */
	    fprintf(stderr,"Error in K estimation.  Something impossible ");
	    fprintf(stderr,"Just happened!\n");
	    exit(4);
	  }
	}
      }
      else{                        /* Not outside br or tr */
	if(edist(&tl,point)<r){     /* Outside tl but not br or tr */
	  if(edist(&bl,point)<r){   /* Outside tl & bl but not br or tr */
	    theta=acos(point->y/r) + acos((b-point->y)/r) + pi;
	    theta+=2*acos((a-point->x)/r); /* Since outside some of line x=a */
	    return 1-theta/(2*pi);
	  }
	  else{                     /* Outside tl only */
	    theta=acos(point->x/r) + acos((b-point->y)/r) + pi/2;
	    theta+=2*acos((a-point->x)/r); /* Since outside some of line x=a */
	    if(point->y-r < 0.0){       /* Outside some of line y=0 */
	      theta+=2*acos(point->y/r);
	    }
	    return 1-theta/(2*pi);
	  }
	}
	else{                       /* Not outside br or tr or tl */
	  if(edist(&bl,point)<r){     /* Outside bl only */
	    theta=acos(point->x/r) + acos(point->y/r) + pi/2;
	    theta+=2*acos((a-point->x)/r); /* Since outside some of line x=a */
	    if(point->y+r > b){       /* Outside some of line y=b */
	      theta+=2*acos((b-point->y)/r);
	    }
	    return 1-theta/(2*pi);
	  }
	  else{                     /* Not outside any corners */
	    theta=acos((a-point->x)/r); /* Since outside some of line x=a */
	    if(point->y-r < 0.0){       /* Outside some of line y=0 */
	      theta+=acos(point->y/r);
	    }
	    if(point->y+r > b){       /* Outside some of line y=b */
	      theta+=acos((b-point->y)/r);
	    }
	    if(point->x-r < 0.0){       /* Outside some of line x=0 */
	      theta+=acos(point->x/r);
	    }
	    return 1-theta/pi;
	  }
	}
      }
    }
  }
  else if(point->x - r < 0.0){    /* Not outside line x=a, so not outside br 
				     or tr */
    if(edist(&tl,point)<r){        /* Outside tl but not br or tr */
      if(edist(&bl,point)<r){      /* Outside tl & bl but not br or tr */
	theta=acos(point->y/r) + acos((b-point->y)/r) + pi;
	return 1-theta/(2*pi);
      }
      else{                        /* Outside tl only */
	theta=acos(point->x/r) + acos((b-point->y)/r) + pi/2;
	if(point->y-r < 0.0){       /* Outside some of line y=0 */
	  theta+=2*acos(point->y/r);
	}
	return 1-theta/(2*pi);
      }
    }
    else{                          /* Not outside tl or br or tr */
      if(edist(&bl,point)<r){      /* Outside bl only */
	theta=acos(point->x/r) + acos(point->y/r) + pi/2;
	if(point->y+r > b){       /* Outside some of line y=b */
	  theta+=2*acos((b-point->y)/r);
	}
	return 1-theta/(2*pi);
      }
      else{                     /* Not outside any corners */
	theta=acos(point->x/r); /* Since outside some of line x=0 */
	if(point->y-r < 0.0){       /* Outside some of line y=0 */
	  theta+=acos(point->y/r);
	}
	if(point->y+r > b){       /* Outside some of line y=b */
	  theta+=acos((b-point->y)/r);
	}
	return 1-theta/pi;
      }
    }
  }
  else if(point->y - r < 0.0){    /* Not outside line x=a or line x=0 so not 
				     outside any corners */
    theta=acos(point->y/r); /* Since outside some of line y=0 */
    if(point->y+r > b){       /* Outside some of line y=b */
      theta+=acos((b-point->y)/r);
    }
    return 1-theta/pi;
  }
  else if(point->y + r > b){    /* Not outside line x=a or line x=0 or line 
				   y=0 so not outside any corners */
    theta=acos((b-point->y)/r);
    return 1-theta/pi;
  }

  return 1.0;   /* If not outside the box at all */
}
