/* glpavl.c */

/*----------------------------------------------------------------------
-- Copyright (C) 2000, 2001, 2002 Andrew Makhorin <mao@mai2.rcnet.ru>,
--               Department for Applied Informatics, Moscow Aviation
--               Institute, Moscow, Russia. All rights reserved.
--
-- This file is a part of GLPK (GNU Linear Programming Kit).
--
-- GLPK 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, or (at your option)
-- any later version.
--
-- GLPK 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 GLPK; see the file COPYING. If not, write to the Free
-- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-- 02111-1307, USA.
----------------------------------------------------------------------*/

#include <stddef.h>
#include "glpavl.h"

/*----------------------------------------------------------------------
-- create_avl - create AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- AVLTREE *create_avl(int (*comp)(void *, void *));
--
-- The create_avl routine creates an empty AVL tree that has no nodes.
--
-- The parameter comp specifies a routine to compare node keys (data
-- type to represent keys should be supplied by the user). This routine
-- has the following prototype:
--
-- int comp(void *key1, void *key2);
--
-- where key1 and key2 are pointers to the first key and to the second
-- key respectively that should be compared. This routine should return
-- one of the following values ([p] denotes a value which p points to):
--
-- < 0, if [key1] is less than [key2];
-- = 0, if [key1] and [key2] are identical;
-- > 0, if [key1] is greater than [key2].
--
-- If node keys are character strings, to create AVL tree the following
-- statement may be used:
--
-- ... = create_avl((int (*)(void *, void *))strcmp);
--
-- If the parameter comp is NULL, that is allowed, node keys will not
-- be used, and then searching, inserting, or deleting are possible only
-- by node positions, but not by node keys.
--
-- *Returns*
--
-- The create_avl routine returns a pointer to the created AVL tree. */

AVLTREE *create_avl(int (*comp)(void *, void *))
{     AVLTREE *tree;
      tree = umalloc(sizeof(AVLTREE));
      tree->pool = create_pool(sizeof(AVLNODE));
      tree->comp = comp;
      tree->size = 0;
      tree->root = NULL;
      tree->height = 0;
      return tree;
}

/*----------------------------------------------------------------------
-- insert_by_key - insert new node with given key to AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h:
-- AVLNODE *insert_by_key(AVLTREE *tree, void *key);
--
-- *Description*
--
-- The insert_by_key routine creates a new node with given key and
-- inserts this node to the AVL tree. (To perform this operation the
-- keys comparing routine should be defined.)
--
-- The insertion is performed in any case whether there are nodes with
-- the same key in the tree or not. To check whether the node with the
-- same key already exists the find_by_key routine should be used.
--
-- It's assumed that data representing a key of the inserted node and
-- which key points to has yet been allocated. This allocation should be
-- kept at least while the node is in the tree or while the tree exists.
--
-- After return the fileds type and link of the inserted node will
-- contain binary zeros. These fileds are available through the pointer
-- returned by the insert_by_key routine and may be used for arbitrary
-- purposes.
--
-- *Returns*
--
-- The insert_by_key routine returns a pointer to the inserted node. */

AVLNODE *insert_by_key(AVLTREE *tree, void *key)
{     AVLNODE *p, *q, *r;
      short int flag;
      if (tree->comp == NULL)
         fault("insert_by_key: key comparing routine not defined");
      /* search for an appropriate place to insert the new node */
      p = NULL; q = tree->root;
      while (q != NULL)
      {  p = q;
         if (tree->comp(key, p->key) <= 0)
         {  flag = 0;
            q = p->left;
            p->rank++;
         }
         else
         {  flag = 1;
            q = p->right;
         }
      }
      /* create and insert the new node */
      r = get_atom(tree->pool);
      r->key = key; r->type = 0; r->link = NULL;
      r->rank = 1; r->up = p;
      r->flag = (short int)(p == NULL ? 0 : flag);
      r->bal = 0; r->left = NULL; r->right = NULL;
      tree->size++;
      if (p == NULL)
         tree->root = r;
      else
         if (flag == 0) p->left = r; else p->right = r;
      /* go up to the root and correct all nodes affected by the
         insertion */
      while (p != NULL)
      {  if (flag == 0)
         {  /* the height of the left subtree of [p] is increased */
            if (p->bal > 0)
            {  p->bal = 0;
               break;
            }
            if (p->bal < 0)
            {  rotate_avl(tree, p);
               break;
            }
            p->bal = -1; flag = p->flag; p = p->up;
         }
         else
         {  /* the height of the right subtree of [p] is increased */
            if (p->bal < 0)
            {  p->bal = 0;
               break;
            }
            if (p->bal > 0)
            {  rotate_avl(tree, p);
               break;
            }
            p->bal = +1; flag = p->flag; p = p->up;
         }
      }
      /* if the root reached, the height of entire tree is increased */
      if (p == NULL) tree->height++;
      return r;
}

/*----------------------------------------------------------------------
-- next_node - search for the next node in AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- AVLNODE *next_node(AVLTREE *tree, AVLNODE *node);
--
-- *Description*
--
-- The next_node routine searches for the next node in AVL tree that
-- follows the given node. If the parameter node is NULL, the routine
-- searches for the first node of AVL tree. The next node always has a
-- key that is not less than a key of the given node.
--
-- The next_node routine can be used to visit all nodes of the AVL tree
-- in the given order by means of the following for statement:
--
-- for (n = next_node(t, NULL); n != NULL; n = next_node(t, n)) ...
--
-- *Returns*
--
-- If the parameter node is NULL, the next_node routine returns a
-- pointer to the first node of the AVL tree that has smallest key, or
-- NULL if the AVL tree is empty. If the parameter node points to the
-- given node, the next_node routine returns a pointer to the next node,
-- or NULL if the next node does not exist. */

AVLNODE *next_node(AVLTREE *tree, AVLNODE *node)
{     AVLNODE *p = node, *q;
      if (tree->root == NULL) return NULL;
      q = (p == NULL ? tree->root : p->right);
      if (q == NULL)
      {  /* go up from the left subtree */
         for (;;)
         {  q = p->up;
            if (q == NULL) break;
            if (p->flag == 0) break;
            p = q;
         }
      }
      else
      {  /* go down into the right subtree */
         for (;;)
         {  p = q->left;
            if (p == NULL) break;
            q = p;
         }
      }
      return q;
}

/*----------------------------------------------------------------------
-- prev_node - search for the previous node in AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- AVLNODE *prev_node(AVLTREE *tree, AVLNODE *node);
--
-- *Description*
--
-- The prev_node routine searches for the previous node in AVL tree that
-- precedes the given node. If the parameter node is NULL, the routine
-- searches for the last node of the AVL tree. The previous node always
-- has a key that is not greater than a key of the given node.
--
-- The prev_node routine can be used to visit all nodes of the AVL tree
-- in the given order by means of the following for statement:
--
-- for (n = prev_node(t, NULL); n != NULL; n = prev_node(t, n)) ...
--
-- *Returns*
--
-- If the parameter node is NULL, the prev_node routine returns a
-- pointer to the last node of the AVL tree, or NULL if the AVL tree is
-- empty. If the parameter node points to the given node, the prev_node
-- routine returns a pointer to the previous node, or NULL if the
-- previous node does not exist. */

AVLNODE *prev_node(AVLTREE *tree, AVLNODE *node)
{     AVLNODE *p = node, *q;
      if (tree->root == NULL) return NULL;
      q = (p == NULL ? tree->root : p->left);
      if (q == NULL)
      {  /* go up from the right subtree */
         for (;;)
         {  q = p->up;
            if (q == NULL) break;
            if (p->flag == 1) break;
            p = q;
         }
      }
      else
      {  /* go down into the left subtree */
         for (;;)
         {  p = q->right;
            if (p == NULL) break;
            q = p;
         }
      }
      return q;
}

/*----------------------------------------------------------------------
-- find_by_key - search for the first node with given key in AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- AVLNODE *find_by_key(AVLTREE *tree, void *key);
--
-- *Description*
--
-- The find_by_key routine searches for the first node in the AVL tree
-- that has the given key. (To perform this operation the key comparing
-- routine should be defined.)
--
-- In order to find all other nodes that have the same key the routine
-- next_by_key can be used.
--
-- *Returns*
--
-- The find_by_key routine returns a pointer to the node that has the
-- given key, or NULL if such node does not exist. */

AVLNODE *find_by_key(AVLTREE *tree, void *key)
{     AVLNODE *p, *q;
      int c;
      if (tree->comp == NULL)
         fault("find_by_key: key comparing routine not defined");
      p = tree->root;
      while (p != NULL)
      {  c = tree->comp(key, p->key);
         if (c == 0) break;
         p = (c < 0 ? p->left : p->right);
      }
      if (p != NULL) for (;;)
      {  q = prev_node(tree, p);
         if (q == NULL) break;
         if (tree->comp(q->key, p->key) != 0) break;
         p = q;
      }
      return p;
}

/*----------------------------------------------------------------------
-- next_by_key - search for the next node having same key in AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- AVLNODE *next_by_key(AVLTREE *tree, AVLNODE *node);
--
-- *Description*
--
-- The next_by_key routine searches the next node in the AVL tree that
-- has the same key as the given node. (To perform this operation the
-- key comparing routine should be defined.)
--
-- In order to find the first node that has the given key the routine
-- find_by_key can be used.
--
-- *Returns*
--
-- The next_by_key routine returns a pointer to the next node that has
-- the same key as the given node, or NULL if the next node does not
-- exist. */

AVLNODE *next_by_key(AVLTREE *tree, AVLNODE *node)
{     AVLNODE *p = node, *q;
      if (tree->comp == NULL)
         fault("next_by_key: key comparing routine not defined");
      if (node == NULL)
         fault("next_by_key: node pointer not specified");
      q = p;
      p = next_node(tree, p);
      if (p != NULL) if (tree->comp(p->key, q->key) != 0) p = NULL;
      return p;
}

/*----------------------------------------------------------------------
-- insert_by_pos - insert new node to given position of AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- AVLNODE *insert_by_pos(AVLTREE *tree, int pos);
--
-- *Description*
--
-- The insert_by_pos routine creates a new node and inserts this node
-- to the given position pos of the AVL tree.
--
-- It is necessary that 1 <= pos <= N+1, where N = tree.size is the
-- total number of nodes in the AVL tree before insertion.
--
-- If we renumber all nodes of the AVL tree from 1 to N in that order
-- in which nodes are visited by the next_node routine, then a new node
-- will be placed between nodes that have numbers pos-1 and pos (if pos
-- is 1, a new node is placed before the first node, and if pos is N+1,
-- a new node is placed after the last node). So, after insertion a new
-- node will be placed in the given position pos.
--
-- After return the fileds type and link of the inserted node will
-- contain binary zeros. These fileds are available through the pointer
-- returned by the insert_by_key routine and may be used for arbitrary
-- purposes.
--
-- It is not recommended to use the insert_by_pos routine together with
-- other routines that suppose keyed nodes (insert_by_key, find_by_key,
-- next_by_key).
--
-- *Returns*
--
-- The insert_by_pos routine returns a pointer to the inserted node. */

AVLNODE *insert_by_pos(AVLTREE *tree, int pos)
{     AVLNODE *p, *q, *r;
      short int flag;
      if (!(1 <= pos && pos <= tree->size+1))
         fault ("insert_by_pos: invalid position");
      /* search for an appropriate place to insert the new node */
      p = NULL; q = tree->root;
      while (q != NULL)
      {  p = q;
         if (pos <= p->rank)
         {  flag = 0;
            q = p->left;
            p->rank++;
         }
         else
         {  flag = 1;
            q = p->right;
            pos -= p->rank;
         }
      }
      /* create and insert the new node */
      r = get_atom(tree->pool);
      r->key = NULL; r->type = 0; r->link = NULL;
      r->rank = 1; r->up = p;
      r->flag = (short int)(p == NULL ? 0 : flag);
      r->bal = 0; r->left = NULL; r->right = NULL;
      tree->size++;
      if (p == NULL)
         tree->root = r;
      else
         if (flag == 0) p->left = r; else p->right = r;
      /* go up to the root and correct all nodes affected by the
         insertion */
      while (p != NULL)
      {  if (flag == 0)
         {  /* the height of the left subtree of [p] is increased */
            if (p->bal > 0)
            {  p->bal = 0;
               break;
            }
            if (p->bal < 0)
            {  rotate_avl(tree, p);
               break;
            }
            p->bal = -1; flag = p->flag; p = p->up;
         }
         else
         {  /* the height of the right subtree of [p] is increased */
            if (p->bal < 0)
            {  p->bal = 0;
               break;
            }
            if (p->bal > 0)
            {  rotate_avl(tree, p);
               break;
            }
            p->bal = +1; flag = p->flag; p = p->up;
         }
      }
      /* if the root reached, the height of entire tree is increased */
      if (p == NULL) tree->height++;
      return r;
}

/*----------------------------------------------------------------------
-- find_by_pos - search for node placed in given position of AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- AVLNODE *find_by_pos(AVLTREE *tree, int pos);
--
-- *Description*
--
-- The find_by_pos routine searches for a node that is placed in the
-- given position pos of the AVL tree. (Do not use this routine to visit
-- all nodes of the AVL tree, because this is not efficient. It's better
-- to use the next_node or the prev_node routines.)
--
-- *Returns*
--
-- If 1 <= pos <= N, where N = tree.size is the total number of nodes
-- in the AVL tree, the find_by_pos routine returns a pointer to the
-- node placed in the given position pos. Otherwise the routine returns
-- NULL. */

AVLNODE *find_by_pos(AVLTREE *tree, int pos)
{     AVLNODE *p;
      p = tree->root;
      while (p != NULL)
      {  if (pos == p->rank) break;
         if (pos < p->rank)
            p = p->left;
         else
         {  pos -= p->rank;
            p = p->right;
         }
      }
      return p;
}

/*----------------------------------------------------------------------
-- delete_node - delete given node from AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- void delete_node(AVLTREE *tree, AVLNODE *node);
--
-- *Description*
--
-- The delete_node deletes the given node from the AVL tree. It is
-- assumed that the deleted node belongs to the AVL tree; otherwise the
-- behavior is undefined. (This operation is applicable in both cases
-- when nodes have keys and when nodes haven't keys, since the order of
-- nodes in the AVL tree is preserved.)
--
-- Should note that exactly the given node will be deleted, so all
-- references to some other nodes (if they exist) will remain valid.
--
-- Actually the memory allocated to the deleted node will not be freed,
-- the routine just returns that memory to the memory pool to reuse it
-- in the future if it will be necessary. */

void delete_node(AVLTREE *tree, AVLNODE *node)
{     AVLNODE *f, *p = node, *q, *r, *s, *x, *y;
      short int flag;
      if (node == NULL)
         fault("delete_node: node pointer not specified");
      /* if the deleted node has two non-empty subtrees, it should be
         interchanged with the next node that always has at least one
         empty subtree */
      if (p->left == NULL || p->right == NULL) goto skip;
      f = p->up; q = p->left;
      r = next_node(tree, p); s = r->right;
      if (p->right == r)
      {  if (f == NULL)
            tree->root = r;
         else
            if (p->flag == 0) f->left = r; else f->right = r;
         r->rank = p->rank; r->up = f;
         r->flag = p->flag; r->bal = p->bal;
         r->left = q; r->right = p;
         q->up = r;
         p->rank = 1; p->up = r; p->flag = 1;
         p->bal = (short int)(s == NULL ? 0 : +1);
         p->left = NULL; p->right = s;
         if (s != NULL) s->up = p;
      }
      else
      {  x = p->right; y = r->up;
         if (f == NULL)
            tree->root = r;
         else
            if (p->flag == 0) f->left = r; else f->right = r;
         r->rank = p->rank; r->up = f;
         r->flag = p->flag; r->bal = p->bal;
         r->left = q; r->right = x;
         q->up = r; x->up = r; y->left = p;
         p->rank = 1; p->up = y; p->flag = 0;
         p->bal = (short int)(s == NULL ? 0 : +1);
         p->left = NULL; p->right = s;
         if (s != NULL) s->up = p;
      }
skip: /* now the deleted node [p] has at least one empty subtree; go up
         to the root and correct rank field of all nodes affected by the
         deletion */
      q = p; f = q->up;
      while (f != NULL)
      {  if (q->flag == 0) f->rank--;
         q = f; f = q->up;
      }
      /* delete the given node from the AVL tree */
      f = p->up; flag = p->flag;
      q = p->left != NULL ? p->left : p->right;
      if (f == NULL)
         tree->root = q;
      else
         if (flag == 0) f->left = q; else f->right = q;
      if (q != NULL) q->up = f, q->flag = flag;
      tree->size--;
      /* go up to the root again and correct all nodes affected by the
         deletion */
      while (f != NULL)
      {  if (flag == 0)
         {  /* the height of the left subtree of [f] is decreased */
            if (f->bal == 0)
            {  f->bal = +1;
               break;
            }
            if (f->bal < 0)
               f->bal = 0;
            else
            {  f = rotate_avl(tree, f);
               if (f->bal < 0) break;
            }
            flag = f->flag; f = f->up;
         }
         else
         {  /* the height of the right subtree of [f] is decreased */
            if (f->bal == 0)
            {  f->bal = -1;
               break;
            }
            if (f->bal > 0)
               f->bal = 0;
            else
            {  f = rotate_avl(tree, f);
               if (f->bal > 0) break;
            }
            flag = f->flag; f = f->up;
         }
      }
      /* if the root reached, the height of entire tree is decreased */
      if (f == NULL) tree->height--;
      /* returns the memory allocated to the deleted node to the memory
         pool of the corresponding AVL tree */
      free_atom(tree->pool, p);
      return;
}

/*----------------------------------------------------------------------
-- rotate_avl - restore correct balance of AVL subtree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- AVLNODE *rotate_avl(AVLTREE *tree, AVLNODE *node);
--
-- The rotate_avl routine restores the correct balance of the subtree
-- specified by its root which node points to. It's assumed that the
-- converted subtree belongs to the AVL tree specified by the parameter
-- tree.
--
-- The rotate_avl is auxiliary routine and not intended for independent
-- usage.
--
-- *Returns*
--
-- The rotate_avl routine returns a pointer to the new root node of the
-- converted subtree. */

AVLNODE *rotate_avl(AVLTREE *tree, AVLNODE *node)
{     AVLNODE *f, *p = node, *q, *r, *x, *y;
      /* determine the kind of rotation */
      if (p->bal < 0) goto neg; else goto pos;
neg:  /* negative (left) rotation is needed */
      f = p->up; q = p->left; r = q->right;
      if (q->bal <= 0) goto neg1; else goto neg2;
neg1: /* perform single negative rotation */
      if (f == NULL)
         tree->root = q;
      else
         if (p->flag == 0) f->left = q; else f->right = q;
      p->rank -= q->rank;
      q->up = f; q->flag = p->flag; q->bal++; q->right = p;
      p->up = q; p->flag = 1;
      p->bal = (short int)(-q->bal); p->left = r;
      if (r != NULL) r->up = p, r->flag = 0;
      return q;
neg2: /* perform double negative rotation */
      x = r->left; y = r->right;
      if (f == NULL)
         tree->root = r;
      else
         if (p->flag == 0) f->left = r; else f->right = r;
      p->rank -= (q->rank + r->rank);
      r->rank += q->rank;
      p->bal = (short int)(r->bal >= 0 ? 0 : +1);
      q->bal = (short int)(r->bal <= 0 ? 0 : -1);
      r->up = f; r->flag = p->flag; r->bal = 0;
      r->left = q; r->right = p;
      p->up = r; p->flag = 1; p->left = y;
      q->up = r; q->flag = 0; q->right = x;
      if (x != NULL) x->up = q, x->flag = 1;
      if (y != NULL) y->up = p, y->flag = 0;
      return r;
pos:  /* positive (right) rotation is needed */
      f = p->up; q = p->right; r = q->left;
      if (q->bal >= 0) goto pos1; else goto pos2;
pos1: /* perform single positive rotation */
      if (f == NULL)
         tree->root = q;
      else
         if (p->flag == 0) f->left = q; else f->right = q;
      q->rank += p->rank;
      q->up = f; q->flag = p->flag; q->bal--; q->left = p;
      p->up = q; p->flag = 0;
      p->bal = (short int)(-q->bal); p->right = r;
      if (r != NULL) r->up = p, r->flag = 1;
      return q;
pos2: /* perform double positive rotation */
      x = r->left; y = r->right;
      if (f == NULL)
         tree->root = r;
      else
         if (p->flag == 0) f->left = r; else f->right = r;
      q->rank -= r->rank;
      r->rank += p->rank;
      p->bal = (short int)(r->bal <= 0 ? 0 : -1);
      q->bal = (short int)(r->bal >= 0 ? 0 : +1);
      r->up = f; r->flag = p->flag; r->bal = 0;
      r->left = p; r->right = q;
      p->up = r; p->flag = 0; p->right = x;
      q->up = r; q->flag = 1; q->left = y;
      if (x != NULL) x->up = p, x->flag = 1;
      if (y != NULL) y->up = q, y->flag = 0;
      return r;
}

/*----------------------------------------------------------------------
-- delete_avl - delete AVL tree.
--
-- *Synopsis*
--
-- #include "glpavl.h"
-- void delete_avl(AVLTREE *tree);
--
-- *Description*
--
-- The delete_avl routine deletes the AVL tree freeing all memory
-- allocated to this object. Should note that memory allocated by the
-- user routines to represent keys or any other data connected with
-- nodes will not be freed automatically. */

void delete_avl(AVLTREE *tree)
{       delete_pool(tree->pool);
        ufree(tree);
        return;
}

/* eof */
