#include <stdlib.h>

#include "tree.h"

/* Threaded version of tree, enabling fast inorder traversal. */

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. */
{
  double cmp;
  register point *node;

  if(tree==NULL)
    return npoint;

  node=tree;
  while(1){
    /*    lastnode=node;*/
    if((cmp = node->birth-npoint->birth) < 0){
      if((node->tag & 1) != 0)        /* If node->right a thread */
	break;
      else
	node=node->right;
    }
    else{
      if((node->tag & 2) != 0)        /* If node->left a thread */
	break;
      else
	node=node->left;
    }
  }

  if(cmp<0){
    npoint->right=node->right;
    npoint->left=node;
    node->right=npoint;
    node->tag&=2;               /* node->right no longer a thread */
  }
  else{
    npoint->left=node->left;
    npoint->right=node;
    node->left=npoint;
    node->tag&=1;               /* node->left no longer a thread */
  }
  return tree;
}

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

  npoint->tag=3;
  if(tree==NULL)
    return npoint;

  node=tree;
  while(1){
    if((cmp = node->death-npoint->death) > 0){
      if((node->tag & 1))       /* If node->right a thread */
	break;
      else
	node=node->right;
    }
    else{
      if((node->tag & 2))       /* If node->left a thread */
	break;
      else
	node=node->left;
    }
  }

  if(cmp>0){
    npoint->right=node->right;
    npoint->left=node;
    node->right=npoint;
    node->tag&=2;               /* node->right no longer a thread */
  }
  else{
    npoint->left=node->left;
    npoint->right=node;
    node->left=npoint;
    node->tag&=1;               /* node->left no longer a thread */
  }
  return tree;
}

point *remove_left(point *tree)       /* Delete left-most node of */
				      /* tree, connecting any loose */
				      /* ends created */
{
  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 */

    if(node!=NULL)         /* If we are not deleting the last node */
			   /* in the tree */
      left(node)->left=NULL;     /* Change left thread to NULL.  If */
				 /* deleting root, then left-most */
				 /* node in remaining tree must */
				 /* have been a thread to root.  We */
				 /* can thus use `left' to find */
				 /* it. */
    return node;
  }

  else{                 /* Connect two trees formed by deleting */
			/* node */

    if(node->tag & 1){        /* If right is thread */
      lastnode->left=NULL;
      lastnode->tag|=2;
    }

    else{
      lastnode->left=node->right;
      free(node);          /* Free up memory used by deleted node */
      left(tree)->left=NULL;    /* Change left thread to NULL.  If */
				/* deleting node which has a right */
				/* sub-tree then left-most node in */
				/* sub-tree must have been a thread */
				/* to the node we are deleting.  We */
				/* can thus use `left' to find */
				/* it. */
    }

    return tree;        /* Root unchanged */
  }
}

void applyinorder(point *tree,
		  void (*fn)(point *,void *),
		  void *arg)
{
  register point *node;
  register point *nextnode;

  if(tree == NULL)
    return;
  node=left(tree);
  (*fn)(node,arg);

  while(node->right != NULL){
    nextnode=node->right;
    if((node->tag & 1) == 0){   /* If node->right is not a thread */
      while((nextnode->tag & 2) == 0)  /* while nextnode->left not */
				       /* a thread */
	nextnode=nextnode->left;
    }
    node=nextnode;
    (*fn)(node,arg);
  }
}

int testinorder(point *tree,
		int (*fn)(point *))
{
  register point *node;
  register point *nextnode;
  register int rtnval=0;

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

  while(node->right != NULL){
    nextnode=node->right;
    if(!(node->tag & 1)){       /* If node->right is not a thread */
      while(!(nextnode->tag & 2))  /* while nextnode->left is not a */
				   /* thread */
	nextnode=nextnode->left;
    }
    node=nextnode;
    if((rtnval=(*fn)(node)) != 0)
      return rtnval;
  }
  return rtnval;
}

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

  if(tree == NULL)
    return NULL;

  node=tree;
  while(!(node->tag & 2)){ /* While left child is not a thread */
    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!!! */
{
  register point *node;
  register point *nextnode;
  point *newnode;
  point *newtree=NULL;

  if(tree==NULL)
    return NULL;

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

  /* Now deal with the rest of the tree */
  while(node->right != NULL){
    nextnode=node->right;
    if(!(node->tag & 1)){       /* If node->right is not a thread */
      while(!(nextnode->tag & 2))  /* while nextnode->left is not a */
				   /* thread */
	nextnode=nextnode->left;
    }
    free(node);              /* Free up memory from previous node.  */
			     /* Don't need to do any housekeeping */
			     /* since we are deleting the whole */
			     /* tree and moving only rightwards. */

    node=nextnode;

    newnode=copynode(node);    /* Copy everything except for the */
			       /* pointers and tags (which are */
                               /*initialised ready for insertion) */
                               /*to a new node. */

    newtree=insert_real(newtree,newnode);  /* Insert new node */
  }

  /* 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 ");
    fprintf(stderr,"allocating space for a point in the ");
    fprintf(stderr,"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;
  newpoint->tag=3;

  return newpoint;
}

point *copynode(point *oldnode)   /* Copy oldnode to a new one.  As */
				  /* is suitable for new nodes we */
				  /* initialize pointers.  Tags are */
				  /* initialised automatically upon */
				  /* insertion, so need no */
				  /* initialization/copying, but */
				  /* we set them to 3 (both */
				  /* threads) for safety. */
{
  register point *newnode;

  newnode=(point *)malloc(sizeof(point));
  if(newnode==NULL){
    fprintf(stderr,"Sorry, out of memory error when ");
    fprintf(stderr,"allocating space for transformation ");
    fprintf(stderr,"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.y;
  newnode->min_life=oldnode->min_life;
  newnode->left=NULL;
  newnode->right=NULL;
  newnode->tag=3;

  return newnode;
}

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

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