/*	Copyright (C) 1992 Free Software Foundation, Inc.

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, 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 software; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "sp.h"


/* Splay trees. */
static int raise (tree, key, pop, vpop)
     struct sp_tree * tree;
     void * key;
     sp_popularity_notify pop;
     void * vpop;
{
  sp_comparer cmp = tree->cmp;
  struct sp_tree_node * node = tree->root;

  if (!node)
    return 0;
  while (1)
    {
      int root_cmp = cmp (node->key, key);
      if (!root_cmp)
	{
	  tree->root = node;
	  return 1;
	}
      else
	{
	  int child_i = (root_cmp + 1) / 2;
	  struct sp_tree_node * child = node->kids[child_i];
	  if (!child)
	    {
	      tree->root = node;
	      return 0;
	    }
	  else
	    {
	      int child_cmp = cmp (child->key, key);
	      if (!child_cmp)
		{
		  if (pop)
		    {
		      if (!(node->kids[!child_i] || child->kids[!child_i]))
			pop (0, node, vpop);
		      if (!(child->kids[0] || child->kids[1]))
			pop (1, child, vpop);
		    }
		  node->kids[child_i] = child->kids[!child_i];
		  child->kids[!child_i] = node;
		  tree->root = child;
		  return 1;
		}
	      else
		{
		  int gchild_i = (child_cmp + 1) / 2;
		  struct sp_tree_node * gchild = child->kids[gchild_i];
		  if (!gchild)
		    {
		      node->kids[child_i] = child->kids[!child_i];
		      child->kids[!child_i] = node;
		      tree->root = child;
		      return 0;
		    }
		  /* else... next page */

		  else if (root_cmp == child_cmp)
		    {
		      if (pop)
			{
			  if (!(node->kids[!child_i]
				|| child->kids[!gchild_i]))
			    pop (0, node, vpop);
			  if (!(gchild->kids[0] || gchild->kids[1]))
			    pop (1, gchild, vpop);
			}
		      node->kids[child_i] = child->kids[!gchild_i];
		      child->kids[!gchild_i] = node;
		      child->kids[gchild_i] = gchild->kids[!gchild_i];
		      gchild->kids[!gchild_i] = child;
		      node = gchild;
		    }
		  else
		    {
		      if (pop)
			{
			  if (!(gchild->kids[0] || gchild->kids[1]))
			    pop (1, gchild, vpop);
			  if (!(child->kids[child_i] || gchild->kids[child_i]))
			    pop (0, child, vpop);
			  if (!(node->kids[gchild_i]
				|| gchild->kids[gchild_i]))
			    pop (0, node, vpop);
			}
		      node->kids[child_i] = gchild->kids[gchild_i];
		      child->kids[gchild_i] = gchild->kids[child_i];
		      gchild->kids[child_i] = child;
		      gchild->kids[gchild_i] = node;
		      node = gchild;
		    }
		}
	    }
	}
    }
}

struct sp_tree * sp_tree (cmp)
     sp_comparer cmp;
{
  struct sp_tree * t = (struct sp_tree *)malloc (sizeof (*t));
  if (t)
    {
      t->cmp = cmp;
      t->root = 0;
    }
  return t;
}

struct sp_tree_node * sp_tree_find2 (tree, key, pop, vpop)
     struct sp_tree * tree;
     void * key;
     sp_popularity_notify pop;
     void * vpop;
{
  return raise (tree, key, pop, vpop) ? tree->root : 0;
}

struct sp_tree_node * sp_tree_find (tree, key)
     struct sp_tree * tree;
     void * key;
{
  return sp_tree_find2 (tree, key, 0, 0);
}

struct sp_tree_node * sp_tree_make2 (tree, key, pop, vpop)
     struct sp_tree * tree;
     void * key;
     sp_popularity_notify pop;
     void * vpop;
{
  if (raise (tree, key, 0, 0))
    return tree->root;
  else
    {
      struct sp_tree_node ** pos = &tree->root;
      struct sp_tree_node * n = (struct sp_tree_node *)malloc (sizeof (*n));
      if (!n)
	return 0;
      while (*pos)
	{
	  int cmp = tree->cmp ((*pos)->key, key);
	  int i = (cmp + 1) / 2;
	  pos = &(*pos)->kids[i];
	}
      *pos = n;
      n->kids[0] = n->kids[1] = 0;
      n->data = 0;
      n->key = key;
      return n;
    }
}

struct sp_tree_node * sp_tree_make (tree, key)
     struct sp_tree * tree;
     void * key;
{
  return sp_tree_make2 (tree, key, 0, 0);
}

static void free_sp_tree_node (node, freer)
     struct sp_tree_node * node;
     sp_key_data_freer freer;
{
  if (node)
    {
      freer (node);
      free_sp_tree_node (node->kids[0], freer);
      free_sp_tree_node (node->kids[1], freer);
      free (node);
    }
}

void free_sp_tree (tree, freer)
     struct sp_tree * tree;
     sp_key_data_freer freer;
{
  free_sp_tree_node (tree->root, freer);
  free (tree);
}
	

