//-----------------------------------------------------------------------------
// Copyright © 2003 - Philip Howard - All rights reserved
//
// 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/avl
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// G. M. Adelson-Velskii and E. M. Landis are the inventors of the algorithm
// bearing their initials, AVL.  Their algorithm for height-balanced binary
// search trees is the basis of the code in this package.  Some modification
// is made to the balancing operations, and features are added for practical
// purposes.
//-----------------------------------------------------------------------------

#include "avl_lib.h"

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	avl_balance
//
// purpose	Balance the binary tree branch containing the specified node.
//
// arguments	1 (AVL *) pointer to root of the binary tree
//		2 (avl_link *) pointer to node to do balancing from
//
// returns	(int) number of shuffles performed
//-----------------------------------------------------------------------------
int
avl_balance(
    AVL *		arg_tree
    ,
    avl_link *		arg_link
    )
__PROTO_END__
{

#ifndef AVL_DONT_BALANCE

    avl_link *	b	;
    avl_link *	c	;
    avl_link *	d	;
    avl_link *	e	;
    avl_link *	f	;

    avl_link *	l	;
    avl_link *	h	;

    avl_link *	n	;
    avl_link *	p	;

    int		dl	;
    int		dh	;


#if AVL_CHECK_INTERNAL_ARGS
    if ( ! arg_tree ) {
	return AVL_ERR_BAD_ROOT;
    }
    if ( ! arg_link ) {
	return AVL_ERR_BAD_NODE;
    }
#endif

    //-- This is here only because gcc cannot figure
    //-- out that they will not be used uninitialized.
    b = c = d = e = f = NULL;

    //-------------------------------------------
    // While below top of tree, do the balancing.
    //-------------------------------------------
    n = arg_link;
    while ( n != & ( arg_tree->link ) ) {

	p = n->pa;

	//-- Get lo and hi depths.
	dl = ( l = n->lo ) ? l->depth : 0;
	dh = ( h = n->hi ) ? h->depth : 0;

	//----------------------------------------------------------
	// Collect individual nodes differently for four conditions.
	//----------------------------------------------------------
	if ( l && dl - dh > 1 ) {
	    f = n;
	    if ( l->lo && l->lo->depth > (l->hi?l->hi->depth:0) ) {
		//----------------------------
		//     ___F___       _D_
		//   _D_      G ->  B   F
		//  B   E          A C E G
		// A C
		//----------------------------
		c = ( b = ( d = l )->lo )->hi;
	    } else if ( l->hi ) {
		//----------------------------
		//    ___F___       _D_
		//  _B_      G ->  B   F
		// A   D          A C E G
		//    C E
		//----------------------------
		c = ( d = ( b = l )->hi )->lo;
	    }
	    e = d->hi;
	} else if ( h && dl - dh < -1 ) {
	    b = n;
	    if ( h->lo && h->lo->depth > (h->hi?h->hi->depth:0) ) {
		//----------------------------
		//  ___B___         _D_
		// A      _F_  ->  B   F
		//       D   G    A C E G
		//      C E
		//----------------------------
		e = ( d = ( f = h )->lo )->hi;
	    } else if ( h->hi ) {
		//----------------------------
		//  ___B___          _D_
		// A      _D_   ->  B   F
		//       C   F     A C E G
		//          E G
		//----------------------------
		e = ( f = ( d = h )->hi )->lo;
	    }
	    c = d->lo;
	} else {

	    //-- Recompute depths of unchanged structure.
	    if ( dl < dh ) dl = dh;
	    ++ dl;

	    //-- If depth is the same, balancing is done.
	    if ( n->depth == dl ) break;

	    //-- Put new depth in link.
	    n->depth = dl;

	    //-- Continue balancing from parent.
	    n = p;
	    continue;
	}

	//------------------------------
	// Reconstruct in balanced form:
	//      _D_
	// ->  B   F
	//    A C E G
	//------------------------------
	if ( c ) c->pa = b;
	if ( e ) e->pa = f;

	b->hi = c;
	b->pa = d;

	f->pa = d;
	f->lo = e;

	d->pa = p;
	d->lo = b;
	d->hi = f;

	if ( p->lo == n ) p->lo = d;
#if AVL_PARANOID_LOGIC
	else if ( p->hi != n ) {
	    return avl_abort( arg_tree, n, "avl_balance(): orphan node being balanced" );
	}
#endif /* AVL_PARANOID_LOGIC */
	else p->hi = d;

	//-- Recompute depths of changed structure.
	dl = c ? c->depth : 0;
	if ( b->lo && b->lo->depth > dl ) dl = b->lo->depth;
	++ dl;
	b->depth = dl;

	dh = e ? e->depth : 0;
	if ( f->hi && f->hi->depth > dh ) dh = f->hi->depth;
	++ dh;
	f->depth = dh;

	if ( dl < dh ) dl = dh;
	++ dl;

	//-- If depth is the same as before,  balancing is done.
	if ( dl == n->depth ) {
	    break;
	}

	//-- Put new depth in link.
	d->depth = dl;

	//-- Continue balancing from parent.
	n = p;
	continue;
    }

#if 0
    //-- Update root depth.  Do we really need this?
    if ( n == & ( arg_tree.link ) ) {
	n->depth = ( ( n->hi ) ? ( n->hi->depth ) : 0 ) + 1;
    }
#endif

#endif /* AVL_DONT_BALANCE */

    return 0;
}

