#include <stdlib.h>

#include "tree.h"

point *insert_dom(point *tree,        /* Insert a new node in the tree */
		  point *npoint)      /* according to it's birth time.  
					 Smallest birth time is left-most. */
{
  register point *node;
  register point *lastnode;
  double cmp;

  if(tree==NULL)
    return npoint;
  node=tree;
  while(node != NULL){
    lastnode=node;
    cmp = lastnode->birth-npoint->birth;
    if(cmp<0)
      node=lastnode->right;
    else
      node=lastnode->left;
  }
  if(cmp<0)
    lastnode->right=npoint;
  else
    lastnode->left=npoint;
  return tree;
}

point *insert_real(point *tree,       /* Insert a new node in the tree */
		   point *npoint)     /* according to it's death time.  
					 Largest death time is left-most. */
{
  register point *node;
  register point *lastnode;
  double cmp;

  if(tree==NULL)
    return npoint;
  node=tree;
  while(node != NULL){
    lastnode=node;
    cmp = lastnode->death-npoint->death;
    if(cmp>0)
      node=lastnode->right;
    else
      node=lastnode->left;
  }
  if(cmp>0)
    lastnode->right=npoint;
  else
    lastnode->left=npoint;
  return tree;
}

point *remove_left(point *tree)
{
  register point *node;
  register point *lastnode;

  if(tree == NULL){     /* Something's wrong --- no nodes in tree! */
    fprintf(stderr,"No nodes in tree so cannot delete left-most!");
    exit(3);
  }

  node=tree;
  lastnode=node;                  /* In case we're deleting root (see below) */
  while(node->left != NULL){      /* Descend left branch of tree */
    lastnode=node;
    node=lastnode->left;
  }

  if(node==tree){       /* If deleting root node */
    node=lastnode->right;
    free(lastnode);        /* Free up memory used by deleted node */
    return node;
  }
  else{                 /* Connect two trees formed by deleting node */
    lastnode->left=node->right;
    free(node);            /* Free up memory used by deleted node */
    return tree;        /* Root unchanged */
  }
}

void applyinorder(point *tree,
		 void (*fn)(point *,void *),
		 void *arg)
{
  if(tree == NULL)
    return;
  applyinorder(tree->left,fn,arg);
  (*fn)(tree,arg);
  applyinorder(tree->right,fn,arg);
}

int testinorder(point *tree,
		int (*fn)(point *))
{
  int rtnval=0;
  
  if(tree == NULL)
    return 0;

  if((rtnval=testinorder(tree->left,fn)) != 0)
    return rtnval;

  if((rtnval=(*fn)(tree)) != 0)
    return rtnval;

  return testinorder(tree->right,fn);
}

point *left(point *tree) /* Find left-most node of tree */
{
  register point *node;

  if(tree == NULL)
    return NULL;

  node=tree;
  while(node->left != NULL){
    node=node->left;
  }
  return node;
}

point *birth_to_death(point *tree) /* Transform tree from one ordered by 
				      birth times to one ordered by death 
				      times.  WARNING: This function 
				      deletes the old tree!!! */
{
  point *node;
  point *newnode;
  point *newtree=NULL;

  if(tree==NULL)
    return NULL;

  /* First, set up the root. */
  node=left(tree);
  newtree=copynode(node);

  /* Now remove node now used as root of new tree */
  tree=remove_left(tree);

  /* Now deal with the rest of the tree */
  while(tree!=NULL){
    node=left(tree);
    newnode=copynode(node);

    tree=remove_left(tree);

    newtree=insert_real(newtree,newnode);
  }

  /* Return the re-ordered tree. */
  return newtree;
}

point *newnode(double deathtime,    /* Initialize new point. */
	       long *tseed,         /* min_life is NOT initialized */
	       long *mseed,         /* by this routine */
	       long *pseed
	       )
{
  register point *newpoint;

  newpoint=(point *)malloc(sizeof(point));
  if(newpoint==NULL){
    fprintf(stderr,"Sorry, out of memory error when allocating ");
    fprintf(stderr,"space for a point in the dominating process\n");
  }
  newpoint->death=deathtime;
  newpoint->birth=deathtime+exp1(tseed);
  newpoint->mark=rand1(mseed);
  newpoint->pos.x=rand1(pseed);
  newpoint->pos.y=rand1(pseed);
  newpoint->left=NULL;
  newpoint->right=NULL;

  return newpoint;
}

point *copynode(point *oldnode)   /* Copy oldnode to a new one.  As is 
				     suitable for new nodes we initialize 
				     pointers. */
{
  register point *newnode;

  newnode=(point *)malloc(sizeof(point));
  if(newnode==NULL){
    fprintf(stderr,"Sorry, out of memory error when allocating ");
    fprintf(stderr,"space for transformation of tree structure\n");
    exit(2);
  }
  newnode->death=oldnode->death;
  newnode->birth=oldnode->birth;
  newnode->mark=oldnode->mark;
  newnode->pos.x=oldnode->pos.x;
  newnode->pos.y=oldnode->pos.x;
  newnode->min_life=oldnode->min_life;
  newnode->left=NULL;
  newnode->right=NULL;

  return newnode;
}

void printcoord(point *p,      /* Print the x and y coordinates according */
		void *arg)     /* to the format passed in arg */
{
  char *fmt;

  fmt = (char *) arg;
  printf(fmt,p->pos.x,p->pos.y);
}

void tmpnode(point *tmpnode,    /* Initialize temporary point */
	     double deathtime,
	     long *tseed,
	     long *mseed,
	     long *pseed
	     )
{
  tmpnode->death=deathtime;
  tmpnode->birth=deathtime+exp1(tseed);
  tmpnode->mark=rand1(mseed);
  tmpnode->pos.x=rand1(pseed);
  tmpnode->pos.y=rand1(pseed);
}
