/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/



#include	"avt.h"

static void set_level(AVT_NODE * avt);
static AVT_NODE * _avt_delete_small(AVT_NODE**);
static AVT_NODE * _avt_delete_large(AVT_NODE**);

static void
set_level(AVT_NODE * avt)
{
int lev1,lev2;
	if ( avt == 0 )
		return;
	if ( avt->large )
		lev1 = avt->large->level;
	else	lev1 = 0;
	if ( avt->small )
		lev2 = avt->small->level;
	else	lev2 = 0;
	if ( lev1 > lev2 )
		avt->level = lev1 + 1;
	else	avt->level = lev2 + 1;
}

static void
avt_valance(AVT_NODE ** avtp)
{
AVT_NODE * b1, * b2, * b3, * b4, * b5;
int lev1,lev2;
	if ( *avtp == 0 )
		return;
	if ( (*avtp)->large )
		lev1 = (*avtp)->large->level;
	else	lev1 = 0;
	if ( (*avtp)->small )
		lev2 = (*avtp)->small->level;
	else	lev2 = 0;
	if ( lev2 + 1 < lev1 ) {
		b1 = *avtp;
		b2 = b1->large;
		b3 = b1->small;
		b4 = b2->large;
		b5 = b2->small;

		*avtp = b2;
		b2->small = b1;
		b1->large = b5;

		set_level(b1);
		set_level(b2);
	}
	else if ( lev1 + 1 < lev2 ) {
		b1 = *avtp;
		b2 = b1->small;
		b3 = b1->large;
		b4 = b2->small;
		b5 = b2->large;

		*avtp = b2;
		b2->large = b1;
		b1->small = b5;

		set_level(b1);
		set_level(b2);
	}
}

AVT_NODE *
avt_insert(AVT_NODE ** avtp,AVT_NODE * a,int (*cmp)())
{
AVT_NODE * ret;
	if ( *avtp == 0 ) {
		if ( a->data == 0 )
			er_panic("avt_insert(1)");
		*avtp = a;
		a->large = 0;
		a->small = 0;
		a->level = 1;
		return a;
	}
	switch ( (*cmp)((*avtp)->data,a->data) ) {
	case -1:
		ret = avt_insert(&(*avtp)->large,a,cmp);
		break;
	case 0:
		ret = *avtp;
		return ret;
	case 1:
		ret = avt_insert(&(*avtp)->small,a,cmp);
		break;
	}
	set_level(*avtp);
	avt_valance(avtp);
	return ret;
}


/*
		b4
	b2
		b5
b1

	b3


	b4
b2
		b5*
	b1*
		b3
*/

AVT_NODE *
avt_search(AVT_NODE * a,void * data,int (*cmp)())
{
	for ( ; a ; ) {
		switch ( (*cmp)(a->data,data) ) {
		case -1:
			a = a->large;
			break;
		case 0:
			return a;
		case 1:
			a = a->small;
			break;
		default:
			er_panic("avt_search(1)");
		}
	}
	return 0;
}


int
avt_trace_from_small(AVT_NODE * a,int (*func)(),void * work)
{
int ret;
	if ( a == 0 )
		return 0;
	if ( a->small ) {
		ret = avt_trace_from_small(a->small,func,work);
		if ( ret )
			return ret;
	}
	ret = (*func)(a,work);
	if ( ret )
		return ret;
	if ( a->large ) {
		ret = avt_trace_from_small(a->large,func,work);
		if ( ret )
			return ret;
	}
	return 0;
}

int
avt_trace_from_large(AVT_NODE * a,int (*func)(),void * work)
{
int ret;
	if ( a == 0 )
		return 0;
	if ( a->large ) {
		ret = avt_trace_from_large(a->large,func,work);
		if ( ret )
			return ret;
	}
	ret = (*func)(a,work);
	if ( ret )
		return ret;
	if ( a->small ) {
		ret = avt_trace_from_large(a->small,func,work);
		if ( ret )
			return ret;
	}
	return 0;
}


static AVT_NODE *
_avt_delete_small(AVT_NODE ** ap)
{
AVT_NODE * ret;
	if ( (*ap)->small ) {
		ret = _avt_delete_small(&(*ap)->small);
		set_level(*ap);
		return ret;
	}
	ret = *ap;
	*ap = (*ap)->large;
	avt_valance(ap);
	return ret;
}

static AVT_NODE *
_avt_delete_large(AVT_NODE ** ap)
{
AVT_NODE * ret;
	if ( (*ap)->large ) {
		ret = _avt_delete_large(&(*ap)->large);
		set_level(*ap);
		return ret;
	}
	ret = *ap;
	*ap = (*ap)->small;
	avt_valance(ap);
	return ret;
}

AVT_NODE *
avt_delete(AVT_NODE ** avtp,void * data,int (*cmp)())
{
AVT_NODE * ret,* a;

	if ( *avtp == 0 )
		return 0;
	switch ( (*cmp)((*avtp)->data,data) ) {
	case -1:
		ret = avt_delete(&(*avtp)->large,data,cmp);
		set_level(*avtp);
		return ret;
	case 0:
		break;
	case 1:
		ret = avt_delete(&(*avtp)->small,data,cmp);
		set_level(*avtp);
		return ret;
	default:
		er_panic("avt_delete(!)");
	}
	ret = *avtp;
	if ( (*avtp)->large == 0 )
		*avtp = (*avtp)->small;
	else if ( (*avtp)->small == 0 )
		*avtp = (*avtp)->large;
	else if ( (*avtp)->small->level < (*avtp)->large->level ) {
		a = _avt_delete_small(&(*avtp)->large);
		a->large = (*avtp)->large;
		a->small = (*avtp)->small;
		*avtp = a;
	}
	else {
		a = _avt_delete_large(&(*avtp)->small);
		a->large = (*avtp)->large;
		a->small = (*avtp)->small;
		*avtp = a;
	}
	set_level(*avtp);
	avt_valance(avtp);
	return ret;
}


typedef struct avt_test {
	void *		data;
	int 		(*cmp)();
} AVT_TEST;

int test_func(AVT_NODE * a,AVT_TEST * w)
{
int lev1,lev2;
	if ( w->data == 0 ) {
		w->data = a->data;
		return 0;
	}
	if ( (*w->cmp)(a->data,w->data) < 0 )
		er_panic("avt error 1");
	w->data = a->data;
	if ( a->large )
		lev1 = a->large->level;
	else	lev1 = 0;
	if ( a->small )
		lev2 = a->small->level;
	else lev2 = 0;
	if ( a->level <= lev1 )
		er_panic("avt error 2");
	if ( a->level <= lev2 )
		er_panic("avt error 3");
	if ( lev1+1 != a->level && lev2+1 != a->level )
		er_panic("avt error 4");
	return 0;
}


void
avt_test(AVT_NODE * a,int (*cmp)())
{
AVT_TEST test;
	test.data = 0;
	test.cmp = cmp;
	avt_trace_from_small(a,test_func,&test);
}
