/* avltree.hh
 * This file belongs to Worker, a filemanager for UNIX/X11.
 * Copyright (C) 2004,2008 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifndef AVLTREE_HH
#define AVLTREE_HH

#include "aguixdefs.h"

template<class T1, class T2>
class AVLTree
{
public:
    AVLTree()
    {
        root = NULL;
    }
  
    ~AVLTree()
    {
        clear();
    }
  
    void insert( T1 k, T2 v )
    {
        avltree_node_t *newnode, *curnode, *tempnode;

        newnode = new avltree_node_t;
        newnode->data = v;
        newnode->key = k;
        newnode->parent = NULL;
        newnode->left = NULL;
        newnode->right = NULL;
        newnode->height = 0;

        if ( root == NULL ) {
            root = newnode;
        } else {
            curnode = root;
            for(;;) {
                if ( k <= curnode->key ) {
                    // left side
                    if ( curnode->left != NULL ) {
                        curnode = curnode->left;
                    } else {
                        curnode->left = newnode;
                        break;
                    }
                } else {
                    // right side
                    if ( curnode->right != NULL ) {
                        curnode = curnode->right;
                    } else {
                        curnode->right = newnode;
                        break;
                    }
                }
            }
            newnode->parent = curnode;

            // curnode is parent, newnode is child
            // now correct balance
            tempnode = curnode;
            while ( tempnode != NULL ) {
                calcHeight( tempnode );
                if ( balance( tempnode ) != 0 ) break;
                tempnode = tempnode->parent;
            }
        }
    }
 
    void remove( T1 k, T2 v )
    {
        T2 tv;

        tv = v;
        remove( k, &tv );
    }

    void remove( T1 k, T2 *pv = NULL )
    {
        avltree_node_t *curnode, *tempnode, *correctnode = NULL;

        // first find node
        curnode = findNode( k, pv );
        if ( curnode == NULL ) return;

        // remove curnode
        if ( ( curnode->left == NULL ) && ( curnode->right == NULL ) ) {
            // curnode is leaf
      
            if ( curnode->parent == NULL ) {
                // no parent -> curnode is root
	
                root = NULL;
            } else {
                if ( curnode->parent->left == curnode ) {
                    curnode->parent->left = NULL;
                } else {
                    curnode->parent->right = NULL;
                }
	
                // correct balance starting with parent
                correctnode = curnode->parent;
            }
        } else if ( ( curnode->left != NULL ) && ( curnode->right == NULL ) ) {
            // only left child
      
            if ( curnode->parent == NULL ) {
                // no parent -> curnode is root
	
                root = curnode->left;
                curnode->left->parent = NULL;
            } else {
                if ( curnode->parent->left == curnode ) {
                    curnode->parent->left = curnode->left;
                } else {
                    curnode->parent->right = curnode->left;
                }
                curnode->left->parent = curnode->parent;
	
                // correct balance starting with parent
                correctnode = curnode->parent;
            }
        } else if ( ( curnode->left == NULL ) && ( curnode->right != NULL ) ) {
            // only right child
      
            if ( curnode->parent == NULL ) {
                // no parent -> curnode is root
	
                root = curnode->right;
                curnode->right->parent = NULL;
            } else {
                if ( curnode->parent->left == curnode ) {
                    curnode->parent->left = curnode->right;
                } else {
                    curnode->parent->right = curnode->right;
                }
                curnode->right->parent = curnode->parent;
	
                // correct balance starting with parent
                correctnode = curnode->parent;
            }
        } else {
            // both childs exists so find greatest node from left tree
            // and replace with curnode
      
            tempnode = curnode->left;
            for(;;) {
                if ( tempnode->right != NULL ) {
                    tempnode = tempnode->right;
                } else break;
            }
      
            // replace curnode with tempnode;
            // tempnode is leaf or has left child
            if ( tempnode->parent->left == tempnode ) {
                tempnode->parent->left = tempnode->left;
            } else {
                tempnode->parent->right = tempnode->left;
            }
            if ( tempnode->left != NULL ) {
                tempnode->left->parent = tempnode->parent;
            }
      
            // correct balance starting with parent
            correctnode = tempnode->parent;
      
            // tempnode is removed so insert for curnode
            if ( curnode->parent == NULL ) {
                // no parent -> curnode is root
	
                root = tempnode;
                tempnode->parent = NULL;
            } else {
                if ( curnode->parent->left == curnode ) {
                    curnode->parent->left = tempnode;
                } else {
                    curnode->parent->right = tempnode;
                }
                tempnode->parent = curnode->parent;
            }
            // although both child existed
            // it might be not true anymore because
            // the replacement node can be a child of curnode
            if ( curnode->left != NULL ) curnode->left->parent = tempnode;
            if ( curnode->right != NULL ) curnode->right->parent = tempnode;
            tempnode->left = curnode->left;
            tempnode->right = curnode->right;
      
            // when we replaced curnode with direct child
            // the correctnode will be curnode
            if ( curnode == correctnode ) {
                correctnode = tempnode;
            }
        }
    
        delete curnode;
    
        // correct balance
        if ( correctnode != NULL ) {
            while ( correctnode != NULL ) {
                calcHeight( correctnode );
                balance( correctnode );
                correctnode = correctnode->parent;
            }
        }
    }
  
    T2 *find( T1 k ) const
    {
        avltree_node_t *curnode;

        curnode = root;
        while ( curnode != NULL ) {
            if ( k == curnode->key ) break;
            else if ( k < curnode->key ) {
                // left side
                curnode = curnode->left;
            } else {
                // right side
                curnode = curnode->right;
            }
        }
        if ( curnode != NULL ) {
            return &(curnode->data);
        }
        return NULL;
    }

    T2 *getRoot() const
    {
        if ( root == NULL ) return NULL;
        return &(root->data);
    }
  
    T2 *findProperty( int(*propcmp)( T2 *p1, T2 *p2 ) );

    void print()
    {
        print( root );
    }
  
    void clear()
    {
        clear( root );
    }
private:
    struct avltree_node_t {
        T1 key;
        T2 data;
        avltree_node_t *parent, *left, *right;
        unsigned int height;
    };
    avltree_node_t *root;

    inline void LL( avltree_node_t *node,
                    avltree_node_t *p1,
                    avltree_node_t *p3,
                    avltree_node_t *p4 ) {
        /* first set new root */
        if ( node->parent == NULL ) {
            root = p1;
        } else {
            if ( node->parent->left == node ) {
                node->parent->left = p1;
            } else {
                node->parent->right = p1;
            }
        }
        /* now set pointer in new root */
        p1->right = node;
        p1->parent = node->parent;
        /* p3 and p4 are childs of p1 => no need to update parents for them */
        /* node is now right child so set the parent and childs */
        node->parent = p1;
        node->left = p4;
        if ( p4 != NULL ) p4->parent = node;
    }
  
    inline void LR( avltree_node_t *node,
                    avltree_node_t *p1,
                    avltree_node_t *p3,
                    avltree_node_t *p4,
                    avltree_node_t *p5,
                    avltree_node_t *p6 ) {
        /* first set new root */
        if ( node->parent == NULL ) {
            root = p4;
        } else {
            if ( node->parent->left == node ) {
                node->parent->left = p4;
            } else {
                node->parent->right = p4;
            }
        }
        /* now set pointer in new root */
        p4->left = p1;
        p4->right = node;
        p4->parent = node->parent;
        p1->right = p5;
        p1->parent = p4;
        if ( p5 != NULL ) p5->parent = p1;
        /* node is now right child so set the parent and childs */
        node->parent = p4;
        node->left = p6;
        if ( p6 != NULL ) p6->parent = node;
    }
 
    inline void RR( avltree_node_t *node,
                    avltree_node_t *p1,
                    avltree_node_t *p3,
                    avltree_node_t *p4 ) {
        /* first set new root */
        if ( node->parent == NULL ) {
            root = p1;
        } else {
            if ( node->parent->left == node ) {
                node->parent->left = p1;
            } else {
                node->parent->right = p1;
            }
        }
        /* now set pointer in new root */
        p1->left = node;
        p1->parent = node->parent;
        /* p3 and p4 are childs of p1 => no need to update parents for them */
        /* node is now right child so set the parent and childs */
        node->parent = p1;
        node->right = p4;
        if ( p4 != NULL ) p4->parent = node;
    }
  
    inline void RL( avltree_node_t *node,
                    avltree_node_t *p1,
                    avltree_node_t *p3,
                    avltree_node_t *p4,
                    avltree_node_t *p5,
                    avltree_node_t *p6 ) {
        /* first set new root */
        if ( node->parent == NULL ) {
            root = p4;
        } else {
            if ( node->parent->left == node ) {
                node->parent->left = p4;
            } else {
                node->parent->right = p4;
            }
        }
        /* now set pointer in new root */
        p4->left = node;
        p4->right = p1;
        p4->parent = node->parent;
        p1->left = p5;
        p1->parent = p4;
        if ( p5 != NULL ) p5->parent = p1;
        /* node is now left child so set the parent and childs */
        node->parent = p4;
        node->right = p6;
        if ( p6 != NULL ) p6->parent = node;
    }

    int balance( avltree_node_t *node )
    {
        int bal;
        avltree_node_t *p1, *p3, *p4, *p5, *p6;
    
        if ( node == NULL ) return 0;
    
        bal = ( ( node->right != NULL ) ? (int)( node->right->height ) : -1 ) -
            ( ( node->left != NULL ) ? (int)( node->left->height ) : -1 );

        if ( bal < -1 ) {
            // bal < -1 is only possible with node->left != NULL
            p1 = node->left;
            p3 = p1->left;
            p4 = p1->right;
            if ( ( p1->left != NULL ) && ( p1->right == NULL ) ) {
                LL( node, p1, p3, p4 );
            } else if ( ( p1->left == NULL ) && ( p1->right != NULL ) ) {
                p5 = p4->left;
                p6 = p4->right;
                LR( node, p1, p3, p4, p5, p6 );
            } else {
                if ( p1->left->height > p1->right->height ) {
                    LL( node, p1, p3, p4 );
                } else {
                    p5 = p4->left;
                    p6 = p4->right;
                    LR( node, p1, p3, p4, p5, p6 );
                }
            }
            calcHeight( node );
            calcHeight( p1 );
            calcHeight( p4 );
            return 1;
        } else if ( bal > 1 ) {
            // bal > 1 is only possible with node->right != NULL
            p1 = node->right;
            p3 = p1->right;
            p4 = p1->left;
            if ( ( p1->right != NULL ) && ( p1->left == NULL ) ) {
                RR( node, p1, p3, p4 );
            } else if ( ( p1->right == NULL ) && ( p1->left != NULL ) ) {
                p5 = p4->right;
                p6 = p4->left;
                RL( node, p1, p3, p4, p5, p6 );
            } else {
                if ( p1->right->height > p1->left->height ) {
                    RR( node, p1, p3, p4 );
                } else {
                    p5 = p4->right;
                    p6 = p4->left;
                    RL( node, p1, p3, p4, p5, p6 );
                }
            }
            calcHeight( node );
            calcHeight( p1 );
            calcHeight( p4 );
            return 1;
        }
        return 0;
    }

    inline void calcHeight( avltree_node_t *node )
    {
        unsigned int i, j;

        if ( node == NULL ) return;
    
        i = ( ( node->right != NULL ) ? ( node->right->height + 1 ) : 0 );
        j = ( ( node->left != NULL ) ? ( node->left->height + 1 ) : 0 );
        node->height = a_max( i, j );
    }
    void print( avltree_node_t *node )
    {
        if ( node == NULL ) return;
        printf( "%d:%d\n", node->key, node->data );
        printf( "left:\n" );
        print( node->left );
        printf( "right:\n" );
        print( node->right );
    }
    void clear( avltree_node_t *node )
    {
        if ( node == NULL ) return;

        clear( node->left );
        clear( node->right );

        if ( node->parent != NULL ) {
            if ( node->parent->left == node ) {
                node->parent->left = NULL;
            } else {
                node->parent->right = NULL;
            }
        }

        delete node;
        if ( node == root ) root = NULL;
    }
    avltree_node_t *findNode( T1 k, T2 *pv = NULL )
    {
        avltree_node_t *curnode;

        curnode = root;
        while ( curnode != NULL ) {
            if ( ( k == curnode->key ) &&
                 ( ( pv == NULL ) ||
                   ( *pv == curnode->data ) ) ) break;
            else if ( k <= curnode->key ) {
                // left side
                curnode = curnode->left;
            } else {
                // right side
                curnode = curnode->right;
            }
        }
        return curnode;
    }

};

template<class T1, class T2>
class AVLList
{
public:
    AVLList()
    {
        head = tail = NULL;
        elements = 0;
    }
  
    ~AVLList()
    {
        clear();
    }
  
    void insert( T1 k, T2 v )
    {
        avllist_t *e1;

        e1 = new avllist_t;
        e1->key = k;
        e1->data = v;
        e1->next = head;
        e1->prev = NULL;
        if ( head != NULL ) head->prev = e1;
        else tail = e1;
        head = e1;
        tree.insert( k, e1 );

        elements++;
    }
 
    void remove( T1 k )
    {
        avllist_t **pe1;
        avllist_t *e1;

        pe1 = tree.find( k );
        if ( pe1 == NULL ) return;
        if ( (*pe1) == NULL ) return;

        e1 = *pe1;
        tree.remove( k, pe1 );
    
        if ( e1->next != NULL ) {
            e1->next->prev = e1->prev;
        }
        if ( e1->prev != NULL ) {
            e1->prev->next = e1->next;
        }
        if ( e1 == head ) {
            head = e1->next;
        }
        if ( e1 == tail ) {
            tail = e1->prev;
        }

        delete e1;

        elements--;
    }
  
    T2 *find( T1 k ) const
    {
        avllist_t **pe1;

        pe1 = tree.find( k );
        if ( pe1 == NULL ) return NULL;
        if ( (*pe1) == NULL ) return NULL;
        return &((*pe1)->data);
    }
  
    T2 *findToFront( T1 k )
    {
        avllist_t **pe1;
        avllist_t *e1;

        pe1 = tree.find( k );
        if ( pe1 == NULL ) return NULL;
        if ( (*pe1) == NULL ) return NULL;
        e1 = *pe1;

        // move to head if not head
        if ( e1 != head ) {
            // first remove from list
            if ( e1->next != NULL ) {
                e1->next->prev = e1->prev;
            }
            if ( e1->prev != NULL ) {
                e1->prev->next = e1->next;
            }
            // no need to check for head because it is not possible
            if ( e1 == tail ) {
                tail = e1->prev;
            }

            // now insert to head
            e1->next = head;
            e1->prev = NULL;
            if ( head != NULL ) head->prev = e1;
            head = e1;
        }
        return &(e1->data);
    }

    size_t size() const
    {
        return elements;
    }

    T2 *getLast() const
    {
        if ( tail == NULL ) return NULL;
        return &(tail->data);
    }

    void removeLast()
    {
        avllist_t *e1;

        if ( tail == NULL ) return;

        e1 = tail;

        tree.remove( e1->key, &e1 );

        if ( tail->prev != NULL ) {
            tail->prev->next = NULL;
        }
        if ( tail == head ) head = NULL;
        tail = tail->prev;

        delete e1;

        elements--;
    }

    T2 *getRoot() const
    {
        avllist_t **e1;

        e1 = tree.getRoot();
        if ( e1 == NULL ) return NULL;
        if ( (*e1) == NULL ) return NULL;

        return &((*e1)->data);
    }
  
    void print()
    {
        tree.print();
    }
  
    void clear()
    {
        avllist_t *e1, *e2;

        tree.clear();
        e1 = head;
        while ( e1 != NULL ) {
            e2 = e1->next;
            delete e1;
            e1 = e2;
        }
        head = tail = NULL;
        elements = 0;
    }
private:
    struct avllist_t {
        T1 key;
        T2 data;
        avllist_t *next, *prev;
    };
    avllist_t *head, *tail;
    AVLTree<T1, avllist_t*> tree;
    int elements;
};

#endif

// Local Variables:
// mode:C++
// End:
