/**************************************************************************
 *
 * sptree.c -- Splay tree code
 * Copyright (C) 1994  Gary Eddy, Alistair Moffat and Neil Sharman
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **************************************************************************/


#include	"sysfuncs.h"
#include	"sptree.h"

#define TRACE(w)

#define POOL_SIZE 1024


typedef struct node_rec node;

struct node_rec
  {
    node *left, *rght;
    node *par;
    char *datsun;
  };

struct pool
  {
    int length, pos;
    struct pool *next;
    node nodes[POOL_SIZE];
  };

static node *SP_splay ();

#define	CNULL	((char *) NULL)
#define	NNULL	((node *) NULL)
#define	SNULL	((Splay_Tree *) NULL)


/*
 *    createset():
 *              allocates memory for and initialises a set.
 *
 *      Accesses outside variables:     none
 *
 *      Return value...:                none
 */

Splay_Tree *
SP_createset (int (*cmp) (void *, void *))
{
  Splay_Tree *S;

  if (!(S = (Splay_Tree *) malloc (sizeof (Splay_Tree))))
    return (SNULL);
  S->cmp = cmp;
  S->root = CNULL;
  S->no_of_items = 0;
  S->mem_in_use = sizeof (Splay_Tree);
  S->pool = NULL;
  S->pool_chunks = 1024;
  return S;
}				/* createset() */



static node *
SP_get_node (Splay_Tree * S)
{
  struct pool *p = S->pool;
  if (!p || p->pos == p->length)
    {
      p = malloc (sizeof (struct pool));
      if (!p)
	return NULL;
      S->mem_in_use += sizeof (struct pool);
      p->length = POOL_SIZE;
      p->pos = 0;
      p->next = S->pool;
      S->pool = p;
    }
  return &(p->nodes[p->pos++]);
}



/*
 *    SP_freeset():
 *              free the memory allocated to a splay tree. Note this Does not
 *              free the data in the tree you must do this yourself.
 *
 *      Accesses outside variables:     none
 *
 *      Return value...:                none
 */

void 
SP_freeset (Splay_Tree * S)
{
  while (S->pool)
    {
      struct pool *p = S->pool;
      S->pool = p->next;
      free (p);
    }
  free (S);
}


/*
 *    insert():
 *              inserts the item given into the set given in the order
 *              specified by the comparison function in the set 
 *              definition.
 *
 *      Accesses outside variables:     none
 *
 *      Return value...:                none
 */


void *
SP_insert (void *item, Splay_Tree * S)
{
  int dir = 0;
  register node *curr, *prev;
  node *tmp;

  prev = NNULL;
  curr = (node *) S->root;
  for (; curr;)
    {
      prev = curr;
      dir = S->cmp (item, curr->datsun);
      if (dir < 0)
	curr = curr->left;
      else
	curr = curr->rght;
    }
  if (!(tmp = SP_get_node (S)))
    return NULL;
  tmp->datsun = item;

  if (prev == NNULL)
    S->root = (char *) tmp;
  else if (dir < 0)
    prev->left = tmp;
  else
    prev->rght = tmp;

  tmp->par = prev;
  tmp->left = NNULL;
  tmp->rght = NNULL;
  S->no_of_items++;
  S->root = (char *) SP_splay (tmp);
  return (tmp->datsun);
}				/* insert() */

/*
 *    get_first():
 *              returns a pointer to the first item in the set. If the set 
 *              is empty a NULL pointer is returned.
 *
 *      Accesses outside variables:     none
 */

void *
SP_get_first (Splay_Tree * S)
{
  node *curr, *prev;

  for (curr = (node *) S->root, prev = NNULL; curr;
       prev = curr, curr = curr->left)
    ;				/* NULLBODY */

  if (prev)
    S->root = (char *) SP_splay (prev);
  return (prev ? prev->datsun : CNULL);
}				/* get_first() */

/*
 *    get_next():
 *              returns the in-order successor of the currently fingered
 *              item of the set. If no item is fingered or if there is 
 *              no in-order successor a NULL pointer is returned.
 *
 *      Accesses outside variables:     none
 */

void *
SP_get_next (Splay_Tree * S)
{
  node *curr, *prev;

  curr = (node *) S->root;
  if (!curr || !curr->rght)
    return (CNULL);

  for (prev = curr, curr = curr->rght; curr;
       prev = curr, curr = curr->left)
    ;				/* NULLBODY */

  if (prev)
    S->root = (char *) SP_splay (prev);
  return (prev ? prev->datsun : CNULL);
}				/* get_next() */

/*
 *    deletemin():
 *              removes the first element in the set and returns a
 *              pointer to it. If the set is empty a NULL pointer 
 *              is returned.
 *
 *      Accesses outside variables:     none
 */

void *
SP_deletemin (Splay_Tree * S)
{
  node *tmp;
  char *dat;

  dat = SP_get_first (S);
  if (dat)
    {
      tmp = (node *) S->root;
      S->root = (char *) tmp->rght;
      if (S->root)
	((node *) S->root)->par = NULL;
    }
  S->no_of_items--;
  return dat;
}				/* deletemin() */


/*
 *    member():
 *              searches the given set for an item which matches (has
 *              the same key) and returns a pointer to a copy of that item.
 *              If no matching item is found a NULL pointer is 
 *              returned.
 *
 *      Accesses outside variables:     none
 */

void *
SP_member (void *dat, Splay_Tree * S)
{
  register node *curr;
  register int dir;
  static int calls = 0;

  TRACE ("in member");
  for (curr = (node *) S->root; curr;)
    {
      TRACE ("call to cmp");
      dir = S->cmp (dat, curr->datsun);
      TRACE ("out of cmp");
      if (dir < 0)
	curr = curr->left;
      else if (dir > 0)
	curr = curr->rght;
      else
	{
	  /* splay occasionaly to speed up execution */
	  if ((calls++ % 4) == 0 && curr)
	    S->root = (char *) SP_splay (curr);
	  TRACE ("out of member");
	  return (curr->datsun);
	}
    }
  TRACE ("out of member");
  return (CNULL);
}				/* member() */

#define	ROTATEL(p,q) 					\
	do{						\
		p->par = q->par;			\
		q->par = p;				\
		q->rght = p->left;			\
		if(q->rght){				\
		  q->rght->par = q;			\
		}                                       \
		p->left = q;				\
		if(p->par){				\
		  if(q == p->par->left){		\
		    p->par->left = p;			\
		  }                                     \
		  else{					\
		    p->par->rght = p;			\
		  }                                     \
		}                                       \
	}while(0)

#define	ROTATER(p,q) 					\
	do{						\
		p->par = q->par;			\
		q->par = p;				\
		q->left = p->rght;			\
		if(q->left){				\
		  q->left->par = q;			\
		}                                       \
		p->rght = q;				\
		if(p->par){				\
		  if(q == p->par->left){		\
		    p->par->left = p;			\
		  }                                     \
		  else{		                        \
		    p->par->rght = p;			\
		  }                                     \
		}                                       \
	}while(0)


/*
 *    splay():
 *              i
 *
 *      Accesses outside variables:     i
 *
 *      Return value...:                i
 */

static node *
SP_splay (p)
     register node *p;
{
  register node *q, *g;

  q = p->par;
  /* splay the tree about p */
  while (q != NNULL)
    {
      g = q->par;
      if (p == q->left)
	{
	  if (g == NNULL){
	    ROTATER (p, q);
	  }
	  else if (q == g->left)
	    {
	      ROTATER (q, g);
	      ROTATER (p, q);
	    }
	  else
	    {
	      ROTATER (p, q);
	      ROTATEL (p, g);
	    }
	}
      else
	{
	  if (g == NNULL){
	    ROTATEL (p, q);
	  }
	  else if (q == g->rght)
	    {
	      ROTATEL (q, g);
	      ROTATEL (p, q);
	    }
	  else
	    {
	      ROTATEL (p, q);
	      ROTATER (p, g);
	    }
	}
      q = p->par;
    }
  return (p);
}
