/*
 * AVLTree.c: Source code for the AVLTree library.
 * Copyright (C) 1998  Michael H. Buselli
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * The author of this library can be reached at the following address:
 * Michael H. Buselli
 * 4334 N. Hazel St. #515
 * Chicago, IL  60613-1456
 * Or you can send email to <cosine@cosine.org>.
 * The original web page for this product is:
 * http://www.cosine.org/project/AVLTree/
 *
 * Original AVLTree by Michael H. Buselli <cosine@cosine.org>.
 *
 * Modified 2000-11-28 by Wessel Dankers <wsl@nl.linux.org> to use counts
 * instead of depths, to add the ->next and ->prev and to generally obfuscate
 * the code. Mail me if you found a bug.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "AVLTree.h"
#include "error.h"

static void AVLRebalance(AVLTree *, AVLNode *);

#ifdef AVL_COUNT
#define NODE_COUNT(n)  ((n) ? (n)->count : 0)
#define L_COUNT(n)     (NODE_COUNT((n)->left))
#define R_COUNT(n)     (NODE_COUNT((n)->right))
#define CALC_COUNT(n)  (L_COUNT(n) + R_COUNT(n) + 1)
#endif

#ifdef AVL_DEPTH
#define NODE_DEPTH(n)  ((n) ? (n)->depth : 0)
#define L_DEPTH(n)     (NODE_DEPTH((n)->left))
#define R_DEPTH(n)     (NODE_DEPTH((n)->right))
#define CALC_DEPTH(n)  ((L_DEPTH(n)>R_DEPTH(n)?L_DEPTH(n):R_DEPTH(n)) + 1)
#endif

#ifndef AVL_DEPTH
static int lg(unsigned int u) {
	int r = 1;
	if(!u) return 0;
	if(u & 0xffff0000) { u >>= 16; r += 16; }
	if(u & 0x0000ff00) { u >>= 8; r += 8; }
	if(u & 0x000000f0) { u >>= 4; r += 4; }
	if(u & 0x0000000c) { u >>= 2; r += 2; }
	if(u & 0x00000002) r++;
	return r;
}
#endif

static int AVLCheckBalance(AVLNode *avlnode) {
#ifdef AVL_DEPTH
	int d;
	d = R_DEPTH(avlnode) - L_DEPTH(avlnode);
	return d<-1?-1:d>1?1:0;
#else
/*	int d;
 *	d = lg(R_COUNT(avlnode)) - lg(L_COUNT(avlnode));
 *	d = d<-1?-1:d>1?1:0;
 */
#ifdef AVL_COUNT
	int pl, r;

	pl = lg(L_COUNT(avlnode));
	r = R_COUNT(avlnode);

	if(r>>pl+1)
		return 1;
	if(pl<2 || r>>pl-2)
		return 0;
	return -1;
#else
#error No balancing possible.
#endif
#endif
}

#ifdef AVL_COUNT
unsigned int AVLCount(AVLTree *avltree) {
	return NODE_COUNT(avltree->top);
}

AVLNode *AVLAtIndex(const AVLTree *avltree, unsigned int index) {
	AVLNode *avlnode;
	unsigned int c;

	avlnode = avltree->top;

	while(avlnode) {
		c = L_COUNT(avlnode);

		if(index < c) {
			avlnode = avlnode->left;
		} else if(index > c) {
			avlnode = avlnode->right;
			index -= c+1;
		} else {
			return avlnode;
		}
	}
	return NULL;
}

unsigned int AVLIndexOf(const AVLNode *avlnode) {
	AVLNode *next;
	unsigned int c;

	c = L_COUNT(avlnode);

	while((next = avlnode->parent)) {
		if(avlnode == next->right)
			c += L_COUNT(next) + 1;
		avlnode = next;
	}

	return c;
}
#endif

int AVLCloseSearch(const AVLTree *avltree, const void *item, AVLNode **avlnode) {
	AVLNode *node;
	AVLCompare cmp;
	int c;

	if(!avlnode)
		avlnode = &node;

	node = avltree->top;

	if(!node)
		return *avlnode = NULL, 0;

	cmp = avltree->cmp;

	for(;;) {
		c = cmp(item, node->item);

		if(c < 0) {
			if(node->left)
				node = node->left;
			else
				return *avlnode = node, -1;
		} else if(c > 0) {
			if(node->right)
				node = node->right;
			else
				return *avlnode = node, 1;
		} else {
			return *avlnode = node, 0;
		}
	}
}

/*
 * AVLSearch:
 * Return a pointer to a node with the given item in the AVL tree.
 * If no such item is in the tree, then NULL is returned.
 */
AVLNode *AVLSearch(const AVLTree *avltree, const void *item) {
	AVLNode *node;
	return AVLCloseSearch(avltree, item, &node) ? NULL : node;
}

AVLTree *AVLInitTree(AVLTree *rc, AVLCompare cmp) {
	if(rc) {
		rc->head = NULL;
		rc->tail = NULL;
		rc->top = NULL;
		rc->cmp = cmp;
	}
	return rc;
}

AVLTree *AVLAllocTree(AVLCompare cmp) {
	return AVLInitTree(xmalloc(sizeof(AVLTree)), cmp);
}

void AVLFreeNodes(AVLTree *avltree) {
	avltree->top = avltree->head = avltree->tail = NULL;
}

AVLNode *AVLInitNode(AVLNode *newnode, void *item) {
	if(newnode) {
		newnode->left = NULL;
		newnode->right = NULL;
		newnode->item = item;
		#ifdef AVL_COUNT
		newnode->count = 1;
		#endif
		#ifdef AVL_DEPTH
		newnode->depth = 1;
		#endif
	}
	return newnode;
}

AVLNode *AVLInsertTopNode(AVLTree *avltree, AVLNode *newnode) {
	newnode->prev = newnode->next = newnode->parent = NULL;
	avltree->head = avltree->tail = avltree->top = newnode;
	return newnode;
}

AVLNode *AVLInsertNodeBefore(AVLTree *avltree, AVLNode *node, AVLNode *newnode) {
	if(!node)
		return avltree->tail
			? AVLInsertNodeAfter(avltree, avltree->tail, newnode)
			: AVLInsertTopNode(avltree, newnode);

	if(node->left)
		return AVLInsertNodeAfter(avltree, node->prev, newnode);

	newnode->next = node;
	newnode->parent = node;

	newnode->prev = node->prev;
	if(node->prev)
		node->prev->next = newnode;
	else
		avltree->head = newnode;
	node->prev = newnode;

	node->left = newnode;
	AVLRebalance(avltree, node->parent);
	return newnode;
}

AVLNode *AVLInsertNodeAfter(AVLTree *avltree, AVLNode *node, AVLNode *newnode) {
	if(!node)
		return avltree->head
			? AVLInsertNodeBefore(avltree, avltree->head, newnode)
			: AVLInsertTopNode(avltree, newnode);

	if(node->right)
		return AVLInsertNodeBefore(avltree, node->next, newnode);

	newnode->prev = node;
	newnode->parent = node;

	newnode->next = node->next;
	if(node->next)
		node->next->prev = newnode;
	else
		avltree->tail = newnode;
	node->next = newnode;

	node->right = newnode;
	AVLRebalance(avltree, node->parent);
	return newnode;
}

AVLNode *AVLInsertNode(AVLTree *avltree, AVLNode *newnode) {
	AVLNode *node;

	if(!avltree->top)
		return AVLInsertTopNode(avltree, newnode);

	switch(AVLCloseSearch(avltree, newnode->item, &node)) {
		case -1:
			return AVLInsertNodeBefore(avltree, node, newnode);
		case 1:
			return AVLInsertNodeAfter(avltree, node, newnode);
	}

	return NULL;
}

/*
 * AVLInsert:
 * Create a new node and insert an item there.
 * Returns the new node on success or NULL if no memory could be allocated.
 */
AVLNode *AVLInsert(AVLTree *avltree, void *item) {
	AVLNode *newnode;

	newnode = AVLInitNode(xmalloc(sizeof(AVLNode)), item);
	if(newnode) {
		if(AVLInsertNode(avltree, newnode))
			return newnode;
		errno = EEXIST;
	}
	return NULL;
}

/*
 * AVLDelete:
 * Deletes the given node.
 */
void AVLUnlinkNode(AVLTree *avltree, AVLNode *avlnode) {
	AVLNode *parent;
	AVLNode **superparent;
	AVLNode *subst, *left, *right;
	AVLNode *balnode;

	if(avlnode->prev)
		avlnode->prev->next = avlnode->next;
	else
		avltree->head = avlnode->next;

	if(avlnode->next)
		avlnode->next->prev = avlnode->prev;
	else
		avltree->tail = avlnode->prev;

	parent = avlnode->parent;

	superparent = parent
		? avlnode == parent->left ? &parent->left : &parent->right
		: &avltree->top;

	left = avlnode->left;
	right = avlnode->right;
	if(!left) {
		*superparent = right;
		if(right)
			right->parent = parent;
		balnode = parent;
	} else if(!right) {
		*superparent = left;
		left->parent = parent;
		balnode = parent;
	} else {
		subst = avlnode->prev;
		if(subst == left) {
			balnode = subst;
		} else {
			balnode = subst->parent;
			balnode->right = subst->left;
			if(balnode->right)
				balnode->right->parent = balnode;
			subst->left = left;
			left->parent = subst;
		}
		subst->right = right;
		subst->parent = parent;
		right->parent = subst;
		*superparent = subst;
	}

	AVLRebalance(avltree, balnode);
}

void AVLDeleteNode(AVLTree *avltree, AVLNode *avlnode) {
	if(avlnode) {
		AVLUnlinkNode(avltree, avlnode);
	}
}

void AVLDelete(AVLTree *avltree, const void *item) {
	AVLDeleteNode(avltree, AVLSearch(avltree, item));
}

/*
 * AVLRebalance:
 * Rebalances the AVL tree if one side becomes too heavy.  This function
 * assumes that both subtrees are AVL trees with consistant data.  This
 * function has the additional side effect of recalculating the count of
 * the tree at this node.  It should be noted that at the return of this
 * function, if a rebalance takes place, the top of this subtree is no
 * longer going to be the same node.
 */
void AVLRebalance(AVLTree *avltree, AVLNode *avlnode) {
	AVLNode *child;
	AVLNode *gchild;
	AVLNode *parent;
	AVLNode **superparent;

	parent = avlnode;

	while(avlnode) {
		parent = avlnode->parent;

		superparent = parent
			? avlnode == parent->left ? &parent->left : &parent->right
			: &avltree->top;

		switch(AVLCheckBalance(avlnode)) {
		case -1:
			child = avlnode->left;
			#ifdef AVL_DEPTH
			if(L_DEPTH(child) >= R_DEPTH(child)) {
			#else
			#ifdef AVL_COUNT
			if(L_COUNT(child) >= R_COUNT(child)) {
			#else
			#error No balancing possible.
			#endif
			#endif
				avlnode->left = child->right;
				if(avlnode->left)
					avlnode->left->parent = avlnode;
				child->right = avlnode;
				avlnode->parent = child;
				*superparent = child;
				child->parent = parent;
				#ifdef AVL_COUNT
				avlnode->count = CALC_COUNT(avlnode);
				child->count = CALC_COUNT(child);
				#endif
				#ifdef AVL_DEPTH
				avlnode->depth = CALC_DEPTH(avlnode);
				child->depth = CALC_DEPTH(child);
				#endif
			} else {
				gchild = child->right;
				avlnode->left = gchild->right;
				if(avlnode->left)
					avlnode->left->parent = avlnode;
				child->right = gchild->left;
				if(child->right)
					child->right->parent = child;
				gchild->right = avlnode;
				if(gchild->right)
					gchild->right->parent = gchild;
				gchild->left = child;
				if(gchild->left)
					gchild->left->parent = gchild;
				*superparent = gchild;
				gchild->parent = parent;
				#ifdef AVL_COUNT
				avlnode->count = CALC_COUNT(avlnode);
				child->count = CALC_COUNT(child);
				gchild->count = CALC_COUNT(gchild);
				#endif
				#ifdef AVL_DEPTH
				avlnode->depth = CALC_DEPTH(avlnode);
				child->depth = CALC_DEPTH(child);
				gchild->depth = CALC_DEPTH(gchild);
				#endif
			}
		break;
		case 1:
			child = avlnode->right;
			#ifdef AVL_DEPTH
			if(R_DEPTH(child) >= L_DEPTH(child)) {
			#else
			#ifdef AVL_COUNT
			if(R_COUNT(child) >= L_COUNT(child)) {
			#else
			#error No balancing possible.
			#endif
			#endif
				avlnode->right = child->left;
				if(avlnode->right)
					avlnode->right->parent = avlnode;
				child->left = avlnode;
				avlnode->parent = child;
				*superparent = child;
				child->parent = parent;
				#ifdef AVL_COUNT
				avlnode->count = CALC_COUNT(avlnode);
				child->count = CALC_COUNT(child);
				#endif
				#ifdef AVL_DEPTH
				avlnode->depth = CALC_DEPTH(avlnode);
				child->depth = CALC_DEPTH(child);
				#endif
			} else {
				gchild = child->left;
				avlnode->right = gchild->left;
				if(avlnode->right)
					avlnode->right->parent = avlnode;
				child->left = gchild->right;
				if(child->left)
					child->left->parent = child;
				gchild->left = avlnode;
				if(gchild->left)
					gchild->left->parent = gchild;
				gchild->right = child;
				if(gchild->right)
					gchild->right->parent = gchild;
				*superparent = gchild;
				gchild->parent = parent;
				#ifdef AVL_COUNT
				avlnode->count = CALC_COUNT(avlnode);
				child->count = CALC_COUNT(child);
				gchild->count = CALC_COUNT(gchild);
				#endif
				#ifdef AVL_DEPTH
				avlnode->depth = CALC_DEPTH(avlnode);
				child->depth = CALC_DEPTH(child);
				gchild->depth = CALC_DEPTH(gchild);
				#endif
			}
		break;
		default:
			#ifdef AVL_COUNT
			avlnode->count = CALC_COUNT(avlnode);
			#endif
			#ifdef AVL_DEPTH
			avlnode->depth = CALC_DEPTH(avlnode);
			#endif
		}
		avlnode = parent;
	}
}
