#include "head.h"
#define exp1(tseed) -log(rand1(tseed))
#include "rand01.h"
#include "ssort.h"
#include "dom.h"

/* returns difference between i->birth and j->birth for sorting */
static double compr1(
		    lifetime *i,
		    lifetime *j
		    );

/* returns difference between j and i for sorting */
static double comp2(
		    double *i,
		    double *j
		    );

static void sort2(double *alive,   /* an array of objects to be sorted */
		  long n,          /* number of elements in array */
		  double (*comp)(void *,void *) 
		                   /* pointer to comparison function */
		  );

void dominating(
		long length,            /* Number of data points */
		double time,           /* Value of $-M$ */
		location points[]  /* All the info about all the points */
		)
{
  double t;
  long tseed;
  long mseed;
  long maxev=0;
  long maxalive;
  long alive;
  lifetime *events;
  lifetime **pevents;
  long i,j,k,l;


/* This next chunk calculates the number of points which are alive at 
   any time between -M and 0 at each location `i' in turn */
  /* BEGIN STEP 1 */
  for(i=0;i<length;++i){
    if(!points[i].continuous){
      tseed=points[i].timeseed;

      for(j=0; j < points[i].initlives; ++j)
	exp1(&tseed);
      for(t=0; t <= time; ++j){
	t+=exp1(&tseed)/points[i].lambda;
	exp1(&tseed);
      }
      points[i].events=j-1;               /* Store number of events */
                                          /* for each point */
      if(j-1>maxev)
	maxev=j-1;
    }
  }
  /* END STEP 1 */

/* This next chunk does tons of stuff.  See individual comments in 
   each sub-step */
  /* BEGIN STEP 2 */
  events = (lifetime *)malloc(maxev * sizeof(lifetime));
  pevents = (lifetime **)malloc(maxev * sizeof(lifetime *));
  for(i=0;i<length;++i){

/* This next chunk deals with what happens if the location is not 
   `always on' and there is a non-zero number of points alive between 
   time -M and 0 */
    /* BEGIN STEP A */
    if(!points[i].continuous && points[i].events > 0){

/* This next chunk copies location $i$s markseed and timeseed somewhere 
   temporary */
      tseed=points[i].timeseed;
      mseed=points[i].markseed;

/* This next chunk gives each point which is alive at time 0 a birth 
   time and a mark.  It also points a pointer to it. */

      /* BEGIN STEP I */
      for(j=0;j<points[i].initlives;++j){
        events[j].death=0;
        events[j].birth=exp1(&tseed);
        events[j].mark=rand1(&mseed);
        pevents[j]=&events[j];
      }
      /* END STEP I */

/* This next chunk runs the birth-death process of the dominating 
   process backwards in time from time $0$ to time $-M$, allowing points 
   to be born at rate \texttt{points[i].lambda} and die at rate 1.  Note 
   that `births' are recorded as death times and deaths as birth 
   times since we are initially running the process backwards.  
   Each point which is alive is also given a mark. */
      /* BEGIN STEP II */
      for(t=0;j<points[i].events;++j){
        events[j].death=t=t+exp1(&tseed)/points[i].lambda;
        events[j].birth=t+exp1(&tseed);
        events[j].mark=rand1(&mseed);
        pevents[j]=&events[j];
      }
      /* END STEP II */

/* This next chunk sorts the pointers to the events in ascending order 
   of birth times so that we now have a list ordered by death times 
   and a second ordered by birth times (note that although all 
   times should be negative numbers we have stored them as positive 
   ones for the sake of convenience, so that `ascending order' means 
   that events closest to zero are first in the list, rather than 
   vice-versa).  It then runs through the process to find the max 
   number of points which are alive at any one time. */
      /* BEGIN STEP III */
      if(points[i].events > 1)
	sort((void **)pevents,points[i].events,
	     (double (*)(void *, void *))compr1);
      j=k=alive=maxalive=0;

      while(j<points[i].events){
        if(events[j].death<pevents[k]->birth){
	  ++alive;
	  ++j;
	  if(alive>maxalive)
	    ++maxalive;    /*maxalive=alive since only increase by one */
        }
        else{
	  --alive;
	  ++k;
        }
      }
      points[i].maxalive=maxalive;
      /* END STEP III */

/* This step continues to run through the births until either one is 
   before time -M or there are no births left.  It then stores the 
   birth, death and mark info for the first point to be born after -M. */
      /* BEGIN STEP IV */
      while(pevents[k]->birth<time && (points[i].events > ++k));
      if(k>0){
	points[i].nextbirth.birth = pevents[k-1]->birth;
	points[i].nextbirth.death = pevents[k-1]->death;
	points[i].nextbirth.mark = pevents[k-1]->mark;
      }
      else
	points[i].nextbirth.birth = 0;
      /* END STEP IV */

/* This next chunk stores the number of points which are alive in the 
   dominating process at time -M in the max process, stores death times 
   for all these points, checks which of them will be alive in the 
   minimum process and stores the number and their death times too.  
   It then sorts them in ascending order of death times. */
      /* BEGIN STEP V */
/* \texttt{points[i].life.max} is the number alive in the max process */
      points[i].life.max=points[i].events-k;
      points[i].maxdt = 
	(double *)malloc((maxalive+1)*sizeof(double));
      points[i].mindt = 
	(double *)malloc((maxalive+1)*sizeof(double));
      points[i].maxdt[1]=points[i].mindt[1]=-1;
      points[i].maxdt[0]=points[i].mindt[0]=time+1;
      for(j=l=0;k<points[i].events;++k){
        if(pevents[k]->mark<points[i].pmin)
	  points[i].mindt[1+l++] = points[i].maxdt[1+j++] =
	    pevents[k]->death;
        else
	  points[i].maxdt[1+j++]=pevents[k]->death;
      }
/* \texttt{points[i].life.min} is the number alive in the min process */
      points[i].life.min=l;

      /* SORT DEATH TIMES FOR MIN \& MAX PROCESSES */
      if(points[i].life.max>1)
        sort2((void *)points[i].maxdt,points[i].life.max+1,
	      (double (*)(void *, void *))comp2);
      if(points[i].life.min>1)
        sort2((void *)points[i].mindt,points[i].life.min+1,
	      (double (*)(void *, void *))comp2);
      /* END STEP V */
    }
    /* END STEP A */

/* This next chunk deals with what happens if there are no events, or 
   the location is `always on'. */
    /* BEGIN STEP B */
    else{
      points[i].maxdt =                      /* Allocate space */ 
	(double *)malloc(2*sizeof(double));  /* for max and */
      points[i].mindt =                      /* min dt */
	(double *)malloc(2*sizeof(double));
      points[i].maxdt[1] = 
	points[i].mindt[1]=-1;   /* Indicate that there are no */
                                 /* live points at this */
                                 /* location to die */

      points[i].maxdt[0]=points[i].mindt[0]=time+1;
      points[i].nextbirth.birth=0;   /* Indicate that there are */
                                     /* no births due to happen */

      if(points[i].events < 1 && 
	 !points[i].continuous){      /* If not `always on' */
	points[i].life.max=0;     /* No points at location i */
	points[i].life.min=0;
      }
    }
    /* END STEP B */
  }
  /* END STEP 2 */

  free(events);
  free(pevents);
}

/* compr1 is the procedure pointed to in sort()-ing events by birth time.  It 
   compares two lifetimes pointed to by pointers i and j. Induces smallest 
   first sorting. */
static double compr1(          /* returns difference between i->birth */
		               /* and j->birth for sorting */
		    lifetime *i,
		    lifetime *j
		    )
{
  return(j->birth - i->birth);
}

/* comp2 is the procedure pointed to in sort2()-ing death times in the max 
   process.  It compares two doubles pointed to by pointers i and j. Induces 
   largest first sorting. */
static double comp2(            /* returns difference between *j and */
		                /* *i for sorting */
		    double *i,
		    double *j
		    )
{
  return(*i - *j);
}

static void sort2(double alive[], /* an array of objects to be sorted */
		  long n,            /* number of elements in array */
		  double (*comp)(void *,void *)
		                   /* pointer to comparison function */
		  )
{
  int exp;         /* to be assign such that $2^(exp-1)<n<=2^exp$ */
  int counter;     /* counter to give us the correct gap at each stage */
                   /* in the algorithm */

/* \texttt{gaps} (below) is to be an array of longs of length 'exp'.  
   In it will be stored the distances between numbers 
   which will be k-sorted as we go through the shell 
   sort (see Knuth V3, pp83-95)*/
  long *gaps;
  long gap,i,j,k;
  double temp;


/* frexp stores the next power of two above n in exp.  
   e.g. n=23 gives exp=5, since $2^4=16<23$ and $2^5=32>23$.*/
  frexp((double) n,&exp);

/* allocate memory for gaps to be an array of longs of length exp. */
  gaps = (long *)malloc((exp+1)*sizeof(long));
  *gaps=1;

/* calculate the gaps in the shell sort using the 
   formula $9.4^i-9.2^i$ for even locations in gaps and 
   $4.4^i-6.2^i+1$ for odd locations.  Also set the 
   counter for gaps. */
  for (i=1;(gaps[2*i] = 9*pow((double) 4,(double) i) - 
	    9 * pow((double) 2,(double) i) + 1) < (n/6); ++i){
    gaps[2*i-1] = 
      4*pow((double) 4,(double) i)-6*pow((double) 2,(double) i)+1;
  }
  gaps[2*i-1] = 
    4*pow((double) 4,(double) i) - 6*pow((double) 2,(double) i)+1;
  counter=2*i;

/* This bit here is the actual shell sort sorting 
   algorithm.  The procedure is standard. */
  for (gap = gaps[counter]; counter-- >= 0; gap=gaps[counter])
    for (i = 0; i<gap; i++)
      for (j=0; j<(n-gap-i); j+=gap){
	if((*comp)(&alive[j+i],&alive[j+gap+i])<0){
	  temp=alive[j+gap+i];
	  for(k=j+i;k>=0 && (*comp)(&alive[k],&temp)<0;k-=gap)
	    alive[k+gap]=alive[k];
	  alive[k+gap]=temp;
	}
      }
  free((void *)gaps);
}
