/*
 * splay tree routines
 * By Matthew Luckie
 * U of Waikato 0657.317b 1999
 *
 * Copyright (C) 1999-2017 Matthew Luckie. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY Matthew Luckie ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Matthew Luckie BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#ifndef lint
static const char rcsid[] =
  "$Id: mjl_splaytree.c,v 1.29 2017/04/29 09:45:30 mjl Exp $";
#endif

#include <stdlib.h>
#include <assert.h>

#if defined(DMALLOC)
#include <dmalloc.h>
#endif

#include "mjl_splaytree.h"

/*
 * the splay tree algorithm needs a simple stack to do the work.
 * the implementations of these functions is found at the bottom of this
 * file.
 */
typedef struct splaytree_stack
{
  splaytree_node_t **nodes;
  int i;
  int c;
} splaytree_stack_t;

/*
 * splay tree node data structure
 * conveniently hidden from users of the splay tree.
 */
struct splaytree_node
{
  void              *item;
  splaytree_node_t  *left;
  splaytree_node_t  *right;
};

struct splaytree
{
  splaytree_node_t     *head;
  int                   size;
  splaytree_cmp_t       cmp;
  splaytree_stack_t    *stack;
  splaytree_onremove_t  onremove;
};

static splaytree_stack_t *stack_create(void);
static splaytree_node_t *stack_pop(splaytree_stack_t *stack);
static void  stack_destroy(splaytree_stack_t *stack);
static int   stack_push(splaytree_stack_t *stack, splaytree_node_t *node);
static void  stack_clean(splaytree_stack_t *stack);

#if !defined(NDEBUG) && defined(MJLSPLAYTREE_DEBUG)
static void splaytree_assert2(splaytree_t *tree, splaytree_node_t *node)
{
  int i;

  if(node != NULL)
    {
      if(node->left != NULL)
	{
	  i = tree->cmp(node->left->item, node->item);
	  assert(i < 0);
	  splaytree_assert2(tree, node->left);
	}

      if(node->right != NULL)
	{
	  i = tree->cmp(node->right->item, node->item);
	  assert(i > 0);
	  splaytree_assert2(tree, node->right);
	}
    }
  return;
}

static void splaytree_assert(splaytree_t *tree)
{
  splaytree_assert2(tree, tree->head);
  return;
}
#else
#define splaytree_assert(tree)((void)0)
#endif

/*
 * splaytree_rotate
 *
 * perform the generic treenode-rotate algorithm.
 */
static void splaytree_rotate(splaytree_node_t *above, splaytree_node_t *below)
{
  splaytree_node_t *temp;

  /*
   * above and below must be valid treenode pointers.
   * above must point to the below node
   */
  assert(above != NULL);
  assert(below != NULL);
  assert(above->left == below || above->right == below);

  /*
   * check to see if the below node is to the left of the above or to
   * the right
   */
  if(above->left == below)
    {
      temp = below->right;
      below->right = above;
      above->left = temp;
    }
  else
    {
      temp = below->left;
      below->left = above;
      above->right = temp;
    }

  return;
}

/*
 * splaytree_splay2
 *
 * appropriately splay the treenodes passed in so that the child is moved
 * higher than the other nodes passed in
 */
static void splaytree_splay2(splaytree_node_t *child,
			     splaytree_node_t *parent,
			     splaytree_node_t *grandparent)
{
  /* pre-condition: grandparent points to parent, parent points to child */
  assert(child != NULL);
  assert(parent == NULL || (parent->left == child || parent->right == child));
  assert(grandparent == NULL ||
	 (grandparent->left == parent || grandparent->right == parent));

  /* case 0: access node is root */
  if(parent == NULL)
    {
      return;
    }

  /* case 1: parent is root */
  else if(grandparent == NULL)
    {
      splaytree_rotate(parent, child);
    }

  /*
   * case 2: zig zig - p is not the root and the child and the parent are both
   * left (right) children
   */
  else if((parent->left  == child && grandparent->left  == parent) ||
	  (parent->right == child && grandparent->right == parent))
    {
      splaytree_rotate(grandparent, parent);
      splaytree_rotate(parent, child);
    }

  /*
   * case 3: zig zag - p is not the root and the child is a left(right) child
   * and parent is a right(left) child
   */
  else if((parent->left  == child && grandparent->right == parent) ||
	  (parent->right == child && grandparent->left  == parent))
    {
      if(grandparent->left == parent)
	{
	  splaytree_rotate(parent, child);
	  grandparent->left = child;
	  splaytree_rotate(grandparent, child);
	}
      else
	{
	  splaytree_rotate(parent, child);
	  grandparent->right = child;
	  splaytree_rotate(grandparent, child);
	}
    }

  return;
}

/*
 * splaytree_splay
 *
 * coordinate the calls to splaytree_splay2.
 * the stack contains, in order, the path to the child so that the nodes can
 * be splayed.
 */
static void splaytree_splay(splaytree_t *tree)
{
  splaytree_node_t *child, *parent, *grandparent, *keep;

  child       = stack_pop(tree->stack);
  parent      = stack_pop(tree->stack);
  grandparent = stack_pop(tree->stack);

  /* there has to be at least one entry in the stack */
  assert(child != NULL);

  /* is there only one node in the tree */
  if(parent == NULL)
    {
      tree->head = child;
      return;
    }

  /* splay the node */
  splaytree_splay2(child, parent, grandparent);

  /* it was a simple swap at the root */
  if(grandparent == NULL)
    {
      tree->head = child;
      return;
    }

  /*
   * remember the grandparent so that we can figure out where to relink the
   * splayed child to
   */
  keep = grandparent;

  /* just loop and we will break out when we need to */
  for(;;)
    {
      /* get the parent nodes to the child */
      parent      = stack_pop(tree->stack);
      grandparent = stack_pop(tree->stack);

      /*
       * if the child node is now at the root, break out as the splay is
       * complete
       */
      if(parent == NULL)
	{
	  break;
	}

      assert(parent->left == keep || parent->right == keep);

      /*
       * figure out where to relink the child to
       * (as the grandparent in keep is now down the tree)
       */
      if(parent->left == keep)
	{
	  parent->left = child;
	}
      else
	{
	  parent->right = child;
	}

      /* splay now */
      splaytree_splay2(child, parent, grandparent);

      if(grandparent == NULL)
	{
	  break;
	}

      keep = grandparent;
    }

  /* return the new root of the tree */
  tree->head = child;
  return;
}

/*
 * splaytree_node_alloc
 *
 * creates/mallocs a node and initialises the contents of the node ready to
 * insert to the tree
 */
#ifndef DMALLOC
static splaytree_node_t *splaytree_node_alloc(const void *item)
#else
static splaytree_node_t *splaytree_node_alloc(const void *item,
					      const char *file, const int line)
#endif
{
  splaytree_node_t *node;
  size_t len = sizeof(splaytree_node_t);

#ifndef DMALLOC
  node = (splaytree_node_t *)malloc(len);
#else
  node = (splaytree_node_t *)dmalloc_malloc(file, line, len,
					    DMALLOC_FUNC_MALLOC, 0, 0);
#endif

  if(node != NULL)
    {
      node->left    = NULL;
      node->right   = NULL;
      node->item    = (void *)item;
    }

  return node;
}

/*
 * splaytree_insert2
 *
 * insert the item into the tree.
 * returns 0 if inserted, -1 on error.
 */
#ifndef DMALLOC
static int splaytree_insert2(splaytree_t *tree, const void *item)
#else
static int splaytree_insert2(splaytree_t *tree, const void *item,
			     const char *file, const int line)
#endif
{
  splaytree_node_t *tn, *node;
  int i;

  tn = tree->head;

  for(;;)
    {
      /* put the node into the insert path and try the next level */
      if(stack_push(tree->stack, tn) != 0)
	return -1;

      /* see whether the data belongs to the left, right, or is a duplicate */
      i = tree->cmp(item, tn->item);
      if(i < 0)
	{
	  if(tn->left != NULL)
	    {
	      tn = tn->left;
	      continue;
	    }

	  /* insert the item into the tree here */
#ifndef DMALLOC
	  if((node = splaytree_node_alloc(item)) == NULL)
#else
	  if((node = splaytree_node_alloc(item, file, line)) == NULL)
#endif
	    return -1;

	  if(stack_push(tree->stack, node) != 0)
	    {
	      free(node);
	      return -1;
	    }

	  tn->left = node;
	  break;
	}
      else if(i > 0)
	{
	  if(tn->right != NULL)
	    {
	      tn = tn->right;
	      continue;
	    }

#ifndef DMALLOC
	  if((node = splaytree_node_alloc(item)) == NULL)
#else
	  if((node = splaytree_node_alloc(item, file, line)) == NULL)
#endif
	    return -1;

	  if(stack_push(tree->stack, node) != 0)
	    {
	      free(node);
	      return -1;
	    }

	  tn->right = node;
	  break;
	}
      else
	{
	  /* the data already exists in the tree: do not add it */
	  return -1;
	}
    }

  return 0;
}

/*
 * splaytree_insert
 *
 * insert a value into the splay tree, and return with the tree splayed on
 * that value.  return the node of the item.
 *
 */
#ifndef DMALLOC
splaytree_node_t *splaytree_insert(splaytree_t *tree, const void *item)
#else
splaytree_node_t *splaytree_insert_dm(splaytree_t *tree, const void *item,
				      const char *file, const int line)
#endif
{
  assert(tree != NULL);

  splaytree_assert(tree);

  /*
   * if the tree actually has something in it, then we need to
   * find the place to insert the node and splay on that.
   */
  if(tree->head != NULL)
    {
      stack_clean(tree->stack);

      /*
       * try and insert the item.  can't insert it if an item matching this
       * one is already there
       */
#ifndef DMALLOC
      if(splaytree_insert2(tree, item) != 0)
#else
      if(splaytree_insert2(tree, item, file, line) != 0)
#endif
	{
	  return NULL;
	}

      splaytree_splay(tree);
    }
  else
    {
#ifndef DMALLOC
      if((tree->head = splaytree_node_alloc(item)) == NULL)
#else
      if((tree->head = splaytree_node_alloc(item, file, line)) == NULL)
#endif
	{
	  return NULL;
	}
    }

  tree->size++;

  splaytree_assert(tree);

  return tree->head;
}

/*
 * splaytree_find2
 *
 * find the node with the data item matching.  returns the node, if found.
 */
static splaytree_node_t *splaytree_find2(splaytree_t *tree, const void *item)
{
  splaytree_node_t *tn;
  int i;

  stack_clean(tree->stack);

  tn = tree->head;
  while(tn != NULL)
    {
      /*
       * try and push the node onto the stack.
       * if we don't then we can't splay the node to the top of the tree, so
       * we fail.
       */
      if(stack_push(tree->stack, tn) != 0)
	return NULL;

      /* determine the next node to visit */
      i = tree->cmp(item, tn->item);
      if(i < 0)
	tn = tn->left;
      else if(i > 0)
	tn = tn->right;
      else
	break;
    }

  /* we found it ! */
  return tn;
}

/*
 * splaytree_find
 *
 * finds an item in the tree, and then splays the tree on that value
 */
void *splaytree_find(splaytree_t *tree, const void *item)
{
  if(tree == NULL || tree->head == NULL)
    {
      return NULL;
    }

  splaytree_assert(tree);
  if(splaytree_find2(tree, item) == NULL)
    {
      return NULL;
    }

  splaytree_splay(tree);
  splaytree_assert(tree);
  return tree->head->item;
}

/*
 * splaytree_remove
 *
 * remove the first item in the splaytree
 */
static int splaytree_remove(splaytree_t *tree)
{
  splaytree_node_t *node;
  splaytree_node_t *l, *r;
  splaytree_node_t *temp;

  node = tree->head;
  l = node->left;
  r = node->right;

  /*
   * search for the right most node in the left tree
   * if there are no nodes on the left hand side of the tree, then we just
   * need to shift the head of the tree to whatever is there on the right
   * of it.
   */
  if(l != NULL)
    {
      stack_clean(tree->stack);
      if(stack_push(tree->stack, l) != 0)
	{
	  return -1;
	}

      temp = l;
      while(temp->right != NULL)
	{
	  if(stack_push(tree->stack, temp->right) != 0)
	    {
	      return -1;
	    }
	  temp = temp->right;
	}

      /* bring this node to the top of the tree with a splay operation */
      splaytree_splay(tree);

      /*
       * as the right most node on the left branch has no nodes on the right
       * branch, we connect the right hand branch to it
       */
      tree->head->right = r;
    }
  else
    {
      tree->head = r;
    }

  tree->size--;

  if(tree->onremove != NULL)
    tree->onremove(node->item);

  free(node);
  return 0;
}

/*
 * splaytree_remove_item
 *
 * remove an item from the tree that matches the particular key
 */
int splaytree_remove_item(splaytree_t *tree, const void *item)
{
  /*
   * find the node that we are supposed to delete.
   * if we can't find it, then the remove operation has failed.
   */
  if(splaytree_find2(tree, item) == NULL)
    {
      return -1;
    }

  /*
   * now that we've found it, splay the tree to bring the node we are to
   * delete to the top of the tree and then delete it.
   */
  splaytree_splay(tree);
  return splaytree_remove(tree);
}

/*
 * splaytree_remove_node
 *
 * remove a specific node from the splay tree
 */
int splaytree_remove_node(splaytree_t *tree, splaytree_node_t *node)
{
  /*
   * find the path to the node that we are supposed to delete.  the node
   * that we find has to match what was passed in
   */
  if(splaytree_find2(tree, node->item) != node)
    {
      return -1;
    }

  /*
   * now that we've found it, splay the tree to bring the node we are to
   * delete to the top of the tree and then delete it.
   */
  splaytree_splay(tree);
  return splaytree_remove(tree);
}

/*
 * splaytree_findclosest
 *
 * find a value in the tree as close to the specified one as possible
 */
void *splaytree_findclosest(splaytree_t *tree, const void *item,
			    splaytree_diff_t diff)
{
  splaytree_node_t *ret;
  splaytree_node_t *first, *second;
  int               first_diff, second_diff;

  if(tree == NULL || tree->head == NULL) return NULL;

  /* wow, the value we are looking for is actually in the tree! */
  if((ret = splaytree_find2(tree, item)) != NULL)
    {
      splaytree_splay(tree);
      assert(ret == tree->head);
      return tree->head->item;
    }

  /*
   * we need to get the last two items off the stack and figure out which
   * one of the two is the closest to the one we are looking for
   */
  first  = stack_pop(tree->stack);
  second = stack_pop(tree->stack);

  /* need at least one item in the stack if tree->head != NULL */
  assert(first != NULL);

  /* if there is only one item in the stack, splay? on it and return it */
  if(second == NULL)
    {
      if(stack_push(tree->stack, first) != 0)
	{
	  return NULL;
	}
      splaytree_splay(tree);
      return tree->head->item;
    }

  /* work out which one is closer to the value we are looking for */
  first_diff  = abs(diff(first->item,  item));
  second_diff = abs(diff(second->item, item));

  /*
   * if the first item is closer than the second, put the first back on the
   * stack and the splay on that
   * else put them both back on and splay on that
   */
  if(second_diff > first_diff)
    {
      if(stack_push(tree->stack, second) != 0)
	{
	  return NULL;
	}
    }
  else
    {
      if(stack_push(tree->stack, second) != 0 ||
	 stack_push(tree->stack, first) != 0)
	{
	  return NULL;
	}
    }

  splaytree_splay(tree);
  return tree->head->item;
}

/*
 * splaytree_depth2
 *
 * recursive function to return the depth of the splay tree.
 */
static int splaytree_depth2(splaytree_node_t *tn)
{
  int left = 0;
  int right = 0;

  if(tn == NULL) return 0;

  if(tn->left != NULL)
    {
      left = splaytree_depth2(tn->left) + 1;
    }
  if(tn->right != NULL)
    {
      right = splaytree_depth2(tn->right) + 1;
    }

  return (left > right) ? left : right;
}

/*
 * splaytree_depth
 *
 * returns the longest path (the depth) of the splay tree
 */
int splaytree_depth(splaytree_t *tree)
{
  if(tree == NULL) return -1;
  if(tree->head == NULL) return 0;
  return splaytree_depth2(tree->head) + 1;
}

/*
 * splaytree_free2
 *
 * iterative function used to free a splaytree's nodes.
 */
static void splaytree_free2(splaytree_t *tree, splaytree_free_t free_ptr)
{
  splaytree_node_t *tn = tree->head, *tn2;

  stack_clean(tree->stack);

  while(tn != NULL)
    {
      if(tn->left != NULL)
	{
	  tn2 = tn->left; tn->left = NULL;
	  stack_push(tree->stack, tn);
	  tn = tn2;
	  continue;
	}

      if(tn->right != NULL)
	{
	  tn2 = tn->right; tn->right = NULL;
	  stack_push(tree->stack, tn);
	  tn = tn2;
	  continue;
	}

      if(tree->onremove != NULL) tree->onremove(tn->item);
      if(free_ptr != NULL) free_ptr(tn->item);
      free(tn);

      tn = stack_pop(tree->stack);
    }

  return;
}

/*
 * splaytree_free
 *
 * dellocate the splaytree
 */
void splaytree_free(splaytree_t *tree, splaytree_free_t free_ptr)
{
  if(tree == NULL) return;
  splaytree_free2(tree, free_ptr);
  stack_destroy(tree->stack);
  free(tree);
  return;
}

void splaytree_empty(splaytree_t *tree, splaytree_free_t free_ptr)
{
  if(tree == NULL) return;
  splaytree_free2(tree, free_ptr);
  tree->head = NULL;
  tree->size = 0;
  return;
}

void splaytree_onremove(splaytree_t *tree, splaytree_onremove_t onremove)
{
  tree->onremove = onremove;
  return;
}

void *splaytree_gethead(splaytree_t *tree)
{
  if(tree == NULL || tree->head == NULL)
    {
      return NULL;
    }

  return tree->head->item;
}

void *splaytree_pophead(splaytree_t *tree)
{
  void *item;
  if(tree->head == NULL)
    return NULL;

  item = tree->head->item;
  if(splaytree_remove(tree) != 0)
    return NULL;

  return item;
}

/*
 * splaytree_getrmlb
 *
 * return the right-most item on the left branch of the tree
 */
void *splaytree_getrmlb(splaytree_t *tree)
{
  splaytree_node_t *tn;

  if(tree == NULL || tree->head == NULL || tree->head->left == NULL)
    {
      return NULL;
    }

  tn = tree->head->left;
  while(tn->right != NULL)
    {
      tn = tn->right;
    }

  return tn->item;
}

/*
 * splaytree_getlmrb
 *
 * return the left-most item on the right branch of the tree
 */
void *splaytree_getlmrb(splaytree_t *tree)
{
  splaytree_node_t *tn;

  if(tree == NULL || tree->head == NULL || tree->head->right == NULL)
    {
      return NULL;
    }

  tn = tree->head->right;
  while(tn->left != NULL)
    {
      tn = tn->left;
    }

  return tn->item;
}

/*
 * splaytree_display2
 *
 * recursive function to print the contents of the splaytree, ascii-like.
 */
static void splaytree_display2(splaytree_node_t *tn, splaytree_display_t disp,
			       int pad)
{
  if(tn != NULL)
    {
      splaytree_display2(tn->left, disp, pad+1);
      disp(tn->item, pad);
      splaytree_display2(tn->right, disp, pad+1);
    }

  return;
}

/*
 * splaytree_display
 *
 * print the contents of the splaytree.
 */
void splaytree_display(splaytree_t *tree, splaytree_display_t disp)
{
  if(tree != NULL && disp != NULL)
    {
      splaytree_display2(tree->head, disp, 1);
    }
  return;
}

/*
 * splaytree_inorder
 *
 * call a user-provided function on all items in the splay tree in order
 */
void splaytree_inorder(splaytree_t *tree, splaytree_inorder_t func, void *in)
{
  splaytree_node_t *tn;

  if(tree == NULL || func == NULL)
    return;

  stack_clean(tree->stack);
  tn = tree->head;

  for(;;)
    {
      if(tn != NULL)
	{
	  stack_push(tree->stack, tn);
	  tn = tn->left;
	}
      else if((tn = stack_pop(tree->stack)) != NULL)
	{
	  func(in, tn->item);
	  tn = tn->right;
	}
      else break;
    }

  return;
}

/*
 * splaytree_alloc
 *
 * allocate a splaytree
 */
#ifndef DMALLOC
splaytree_t *splaytree_alloc(splaytree_cmp_t cmp)
#else
splaytree_t *splaytree_alloc_dm(splaytree_cmp_t cmp,
				const char *file, const int line)
#endif
{
  splaytree_t *tree;
  size_t len = sizeof(splaytree_t);

#ifndef DMALLOC
  tree = (splaytree_t *)malloc(len);
#else
  tree = (splaytree_t *)dmalloc_malloc(file,line,len,DMALLOC_FUNC_MALLOC,0,0);
#endif

  if(tree == NULL || (tree->stack = stack_create()) == NULL)
    goto err;

  tree->head     = NULL;
  tree->onremove = NULL;
  tree->size     = 0;
  tree->cmp      = cmp;
  return tree;

 err:
  if(tree != NULL)
    free(tree);
  return NULL;
}

/*
 * splaytree_count
 *
 * return the number of items in the splaytree.
 */
int splaytree_count(splaytree_t *tree)
{
  if(tree == NULL) return -1;
  return tree->size;
}

/*
 * stack_create
 *
 * create a stack that is optimised for dealing with splaytree processing
 */
static splaytree_stack_t *stack_create(void)
{
  splaytree_stack_t *s;

  if((s = (splaytree_stack_t *)malloc(sizeof(splaytree_stack_t))) == NULL)
    {
      return NULL;
    }

  s->i = -1;
  s->c = 128;
  if((s->nodes = malloc(sizeof(splaytree_node_t *) * s->c)) == NULL)
    {
      free(s);
      return NULL;
    }

  return s;
}

/*
 * stack_clean
 *
 * reset the splaytree stack so it is emptied.
 */
static void stack_clean(splaytree_stack_t *s)
{
  s->i = -1;
  return;
}

/*
 * stack_destroy
 *
 * free the memory allocated to the splaytree stack.
 */
static void stack_destroy(splaytree_stack_t *s)
{
  free(s->nodes);
  free(s);
  return;
}

/*
 * stack_push
 *
 * put the node on the splaytree stack, growing the array if necessary.
 */
static int stack_push(splaytree_stack_t *s, splaytree_node_t *node)
{
  splaytree_node_t **nodes;
  size_t size;

  if(s->i+1 == s->c)
    {
      size = sizeof(splaytree_node_t *) * (s->c + 128);
      if((nodes = (splaytree_node_t **)realloc(s->nodes, size)) == NULL)
	{
	  return -1;
	}

      s->c += 128;
      s->nodes = nodes;
    }

  s->nodes[++s->i] = node;
  return 0;
}

/*
 * stack_pop
 *
 * remove the splaytree node at the top of the stack.
 */
static splaytree_node_t *stack_pop(splaytree_stack_t *s)
{
  if(s->i == -1) return NULL;
  return s->nodes[s->i--];
}
