#include <stdlib.h>
#include "tree.h"
#include "ellipse.h"
#include "rand21.h"
#include "process.h"

#define exp2(seed) (-log(rand2(seed)))

point *process(double time,
	       double gamma1,
	       double gamma2,
	       double a1,
	       double a2,
	       double b1,
	       double b2,
	       double exp_param,
	       long seed,
	       long seedtwo,
	       long seedthree,
	       point *points,
	       short *stop
	       )
{
  double ndtime;     /* Next death time */
  double nbtime;     /* Next birth time */
  point *nextpoint=NULL;  /* Next point to be born */
  double p_max;   /* Probability of acceptance in the max process */
  double p_min;   /* Probability of acceptance in the min process */

  /* Initialize next death time. */
  if(points==NULL)                  /* If no points in process */
    ndtime=0;
  else
    ndtime=left(points)->death;

  /* Initialize next birth time. */
  nbtime=time;
  nextpoint=nextbirth(seed,seedtwo,seedthree,nbtime,exp_param);
  if(nextpoint==NULL)      /* If couldn't find any points which */
			   /* were born */
    nbtime=0;              /* t=0 and t=time, set nbtime to zero. */
  else                
    nbtime=nextpoint->birth;

  /*  while next event is before time zero (otherwise finished) */
  while((nbtime > 0) || (ndtime > 0)){

/* If a death is the next event remove the point and... */
    if(nbtime<ndtime){
      points=remove_left(points);
/* Update ndtime. */
      if(points==NULL)
	ndtime=0;
      else
	ndtime=left(points)->death;
    }

/* A potential birth is next event */
    else{

/* Find the probability of accepting point in max and min processes */
      calc_p(points,nextpoint,gamma1,gamma2,a1,a2,b1,b2,&p_max,
	     &p_min);

      if(p_max >= nextpoint->mark){   /* If accept in max process */
	nextpoint->left=NULL;
	nextpoint->right=NULL;               /* add the point. */
	points=insert_real(points,nextpoint);

	if(p_min < nextpoint->mark)   /* If reject in min process */
	  nextpoint->min_life=0;          /* Set min\_life=0 */
	else
	  nextpoint->min_life=1;   /* Otherwise set min\_life to 1 */

	if(ndtime < nextpoint->death) /* If new point dies before */
	  ndtime=nextpoint->death;    /* any other point then */
				      /* update ndtime */
      }

/* If reject point in max process, must be rejecting in min process, 
   so free up space used by potential point */
      else{
	free(nextpoint);
      }

/* Find point born soonest after point just added/rejected */
      nextpoint=nextbirth(seed,seedtwo,seedthree,nbtime,
			  exp_param);

      if(nextpoint==NULL) /* If couldn't find any points which were */
	nbtime=0;         /* born then t=0 and t=nbtime, so set */
                          /* nbtime to zero */
      else
	nbtime=nextpoint->birth;
    }
  }

  if(testinorder(points,difference) != 0)  /* If the min and max */
    *stop=0;                               /* processes are not */
					   /* identical, keep going. */
  else
    *stop=1;                          /* Otherwise, stop. */

  return points;
}

point *nextbirth(long seed,      /* Returns next point to be born */
		 long seedtwo,
		 long seedthree,
		 double lastbirth,   /* Time of previous birth */
		 double exp_param
		 )
{
  register point *newpoint;
  register point *nextpoint;
  point *tmppoint;
  point *tstpoint;
  long iseed;                /* Seed for Poisson parameter for */
			     /* initially alive points */
  long tseed;                /* Seed for birth and death times */
  long pseed;                /* Seed for position of points */
  long mseed;                /* Seed for marks */
  double sum;
  double tmptime;  /* Used when updating nbtime */
  double ldtime;   /* Last death time.  Used when updating nbtime */
  double test1;
  double test2;

  /* Initialisations */
  newpoint=(point *)malloc(sizeof(point));
  tstpoint=nextpoint=(point *)malloc(sizeof(point));
  nextpoint->birth=lastbirth*2;
  nextpoint->death=newpoint->death=0;

  iseed=seed;
  tseed=seed;
  mseed=seedtwo;
  pseed=seedthree;
  tmptime=0;

  /* Initial points */
  test1=sum=exp2(&iseed);
  while(sum<=exp_param){
    sum+=exp2(&iseed);                /* Update Poisson parameter */

    newpoint->birth=exp1(&tseed);
    newpoint->mark=rand1(&mseed);     /* Initialize new point */
    newpoint->pos.x=rand1(&pseed);
    newpoint->pos.y=rand1(&pseed);
    /* If newpoint is born before current point and after previous point */
    if((newpoint->birth > tmptime) && 
       (newpoint->birth < lastbirth)){
      tmppoint=nextpoint;
      nextpoint=newpoint;                /* Update current point. */
      tmptime=newpoint->birth;
      newpoint=tmppoint;
    }
  }

  test2=ldtime=exp1(&tseed)/exp_param;
  while(ldtime < lastbirth){
    newpoint->death=ldtime;
    newpoint->birth=ldtime+exp1(&tseed);
    newpoint->mark=rand1(&mseed);         /* Initialize new point */
    newpoint->pos.x=rand1(&pseed);
    newpoint->pos.y=rand1(&pseed);
    ldtime+=exp1(&tseed)/exp_param;
    if((newpoint->birth > tmptime) && 
       (newpoint->birth < lastbirth)){
      tmppoint=nextpoint;
      nextpoint=newpoint;
      tmptime=newpoint->birth;
      newpoint=tmppoint;
    }
  }

  free(newpoint);

  if(((tstpoint==nextpoint) && (nextpoint->birth > lastbirth)) || 
     ((test1==sum) && (test2==ldtime)))
    return NULL;
  else
    return nextpoint;
}

int difference(point *p)   /* Returns 0 if point is in min process, */ 
			   /* 1 otherwise */
{
  if(p->min_life != 1)
    return 1;
  else
    return 0;
}
