blob: 7144cb569a87af1cd7601956edb627f74ead22a4 [file] [log] [blame]
/*
* Revision Control Information
*
* $Source: /vol/opua/opua2/sis/sis-1.2/common/src/sis/avl/RCS/avl.c,v $
* $Author: sis $
* $Revision: 1.3 $
* $Date: 1994/07/15 23:00:40 $
*
*/
/* LINTLIBRARY */
#include <stdio.h>
#include <stdlib.h>
#include "avl.h"
ABC_NAMESPACE_IMPL_START
#define HEIGHT(node) (node == NIL(avl_node) ? -1 : (node)->height)
#define BALANCE(node) (HEIGHT((node)->right) - HEIGHT((node)->left))
#define compute_height(node) { \
int x=HEIGHT(node->left), y=HEIGHT(node->right); \
(node)->height = MAX(x,y) + 1; \
}
#define COMPARE(key, nodekey, compare) \
((compare == avl_numcmp) ? \
(int) key - (int) nodekey : \
(*compare)(key, nodekey))
#define STACK_SIZE 50
static avl_node *new_node ();
static avl_node *find_rightmost ();
static void do_rebalance ();
static rotate_left ();
static rotate_right ();
static int do_check_tree ();
avl_tree *
avl_init_table (compar)
int (*compar) ();
{
avl_tree *tree;
tree = ALLOC (avl_tree, 1);
tree->root = NIL (avl_node);
tree->compar = compar;
tree->num_entries = 0;
return tree;
}
avl_lookup (tree, key, value_p)
avl_tree *tree;
register char *key;
char **value_p;
{
register avl_node *node;
register int (*compare) () = tree->compar, diff;
node = tree->root;
while (node != NIL (avl_node))
{
diff = COMPARE (key, node->key, compare);
if (diff == 0)
{
/* got a match */
if (value_p != NIL (char *))
*value_p = node->value;
return 1;
}
node = (diff < 0) ? node->left : node->right;
}
return 0;
}
avl_first (tree, key_p, value_p)
avl_tree *tree;
char **key_p;
char **value_p;
{
register avl_node *node;
if (tree->root == 0)
{
return 0; /* no entries */
}
else
{
/* walk down the tree; stop at leftmost leaf */
for (node = tree->root; node->left != 0; node = node->left)
{
}
if (key_p != NIL (char *))
*key_p = node->key;
if (value_p != NIL (char *))
*value_p = node->value;
return 1;
}
}
avl_last (tree, key_p, value_p)
avl_tree *tree;
char **key_p;
char **value_p;
{
register avl_node *node;
if (tree->root == 0)
{
return 0; /* no entries */
}
else
{
/* walk down the tree; stop at rightmost leaf */
for (node = tree->root; node->right != 0; node = node->right)
{
}
if (key_p != NIL (char *))
*key_p = node->key;
if (value_p != NIL (char *))
*value_p = node->value;
return 1;
}
}
avl_insert (tree, key, value)
avl_tree *tree;
char *key;
char *value;
{
register avl_node **node_p, *node;
register int stack_n = 0;
register int (*compare) () = tree->compar;
avl_node **stack_nodep[STACK_SIZE];
int diff, status;
node_p = &tree->root;
/* walk down the tree (saving the path); stop at insertion point */
status = 0;
while ((node = *node_p) != NIL (avl_node))
{
stack_nodep[stack_n++] = node_p;
diff = COMPARE (key, node->key, compare);
if (diff == 0)
status = 1;
node_p = (diff < 0) ? &node->left : &node->right;
}
/* insert the item and re-balance the tree */
*node_p = new_node (key, value);
do_rebalance (stack_nodep, stack_n);
tree->num_entries++;
tree->modified = 1;
return status;
}
avl_find_or_add (tree, key, slot_p)
avl_tree *tree;
char *key;
char ***slot_p;
{
register avl_node **node_p, *node;
register int stack_n = 0;
register int (*compare) () = tree->compar;
avl_node **stack_nodep[STACK_SIZE];
int diff;
node_p = &tree->root;
/* walk down the tree (saving the path); stop at insertion point */
while ((node = *node_p) != NIL (avl_node))
{
stack_nodep[stack_n++] = node_p;
diff = COMPARE (key, node->key, compare);
if (diff == 0)
{
if (slot_p != 0)
*slot_p = &node->value;
return 1; /* found */
}
node_p = (diff < 0) ? &node->left : &node->right;
}
/* insert the item and re-balance the tree */
*node_p = new_node (key, NIL (char));
if (slot_p != 0)
*slot_p = &(*node_p)->value;
do_rebalance (stack_nodep, stack_n);
tree->num_entries++;
tree->modified = 1;
return 0; /* not already in tree */
}
avl_delete (tree, key_p, value_p)
avl_tree *tree;
char **key_p;
char **value_p;
{
register avl_node **node_p, *node, *rightmost;
register int stack_n = 0;
char *key = *key_p;
int (*compare) () = tree->compar, diff;
avl_node **stack_nodep[STACK_SIZE];
node_p = &tree->root;
/* Walk down the tree saving the path; return if not found */
while ((node = *node_p) != NIL (avl_node))
{
diff = COMPARE (key, node->key, compare);
if (diff == 0)
goto delete_item;
stack_nodep[stack_n++] = node_p;
node_p = (diff < 0) ? &node->left : &node->right;
}
return 0; /* not found */
/* prepare to delete node and replace it with rightmost of left tree */
delete_item:
*key_p = node->key;
if (value_p != 0)
*value_p = node->value;
if (node->left == NIL (avl_node))
{
*node_p = node->right;
}
else
{
rightmost = find_rightmost (&node->left);
rightmost->left = node->left;
rightmost->right = node->right;
rightmost->height = -2; /* mark bogus height for do_rebal */
*node_p = rightmost;
stack_nodep[stack_n++] = node_p;
}
FREE (node);
/* work our way back up, re-balancing the tree */
do_rebalance (stack_nodep, stack_n);
tree->num_entries--;
tree->modified = 1;
return 1;
}
static void
avl_record_gen_forward (node, gen)
avl_node *node;
avl_generator *gen;
{
if (node != NIL (avl_node))
{
avl_record_gen_forward (node->left, gen);
gen->nodelist[gen->count++] = node;
avl_record_gen_forward (node->right, gen);
}
}
static void
avl_record_gen_backward (node, gen)
avl_node *node;
avl_generator *gen;
{
if (node != NIL (avl_node))
{
avl_record_gen_backward (node->right, gen);
gen->nodelist[gen->count++] = node;
avl_record_gen_backward (node->left, gen);
}
}
avl_generator *
avl_init_gen (tree, dir)
avl_tree *tree;
int dir;
{
avl_generator *gen;
/* what a hack */
gen = ALLOC (avl_generator, 1);
gen->tree = tree;
gen->nodelist = ALLOC (avl_node *, avl_count (tree));
gen->count = 0;
if (dir == AVL_FORWARD)
{
avl_record_gen_forward (tree->root, gen);
}
else
{
avl_record_gen_backward (tree->root, gen);
}
gen->count = 0;
/* catch any attempt to modify the tree while we generate */
tree->modified = 0;
return gen;
}
avl_gen (gen, key_p, value_p)
avl_generator *gen;
char **key_p;
char **value_p;
{
avl_node *node;
if (gen->count == gen->tree->num_entries)
{
return 0;
}
else
{
node = gen->nodelist[gen->count++];
if (key_p != NIL (char *))
*key_p = node->key;
if (value_p != NIL (char *))
*value_p = node->value;
return 1;
}
}
void
avl_free_gen (gen)
avl_generator *gen;
{
FREE (gen->nodelist);
FREE (gen);
}
static avl_node *
find_rightmost (node_p)
register avl_node **node_p;
{
register avl_node *node;
register int stack_n = 0;
avl_node **stack_nodep[STACK_SIZE];
node = *node_p;
while (node->right != NIL (avl_node))
{
stack_nodep[stack_n++] = node_p;
node_p = &node->right;
node = *node_p;
}
*node_p = node->left;
do_rebalance (stack_nodep, stack_n);
return node;
}
static void
do_rebalance (stack_nodep, stack_n)
register avl_node ***stack_nodep;
register int stack_n;
{
register avl_node **node_p, *node;
register int hl, hr;
int height;
/* work our way back up, re-balancing the tree */
while (--stack_n >= 0)
{
node_p = stack_nodep[stack_n];
node = *node_p;
hl = HEIGHT (node->left); /* watch for NIL */
hr = HEIGHT (node->right); /* watch for NIL */
if ((hr - hl) < -1)
{
rotate_right (node_p);
}
else if ((hr - hl) > 1)
{
rotate_left (node_p);
}
else
{
height = MAX (hl, hr) + 1;
if (height == node->height)
break;
node->height = height;
}
}
}
static
rotate_left (node_p)
register avl_node **node_p;
{
register avl_node *old_root = *node_p, *new_root, *new_right;
if (BALANCE (old_root->right) >= 0)
{
*node_p = new_root = old_root->right;
old_root->right = new_root->left;
new_root->left = old_root;
}
else
{
new_right = old_root->right;
*node_p = new_root = new_right->left;
old_root->right = new_root->left;
new_right->left = new_root->right;
new_root->right = new_right;
new_root->left = old_root;
compute_height (new_right);
}
compute_height (old_root);
compute_height (new_root);
}
static
rotate_right (node_p)
avl_node **node_p;
{
register avl_node *old_root = *node_p, *new_root, *new_left;
if (BALANCE (old_root->left) <= 0)
{
*node_p = new_root = old_root->left;
old_root->left = new_root->right;
new_root->right = old_root;
}
else
{
new_left = old_root->left;
*node_p = new_root = new_left->right;
old_root->left = new_root->right;
new_left->right = new_root->left;
new_root->left = new_left;
new_root->right = old_root;
compute_height (new_left);
}
compute_height (old_root);
compute_height (new_root);
}
static void
avl_walk_forward (node, func)
avl_node *node;
void (*func) ();
{
if (node != NIL (avl_node))
{
avl_walk_forward (node->left, func);
(*func) (node->key, node->value);
avl_walk_forward (node->right, func);
}
}
static void
avl_walk_backward (node, func)
avl_node *node;
void (*func) ();
{
if (node != NIL (avl_node))
{
avl_walk_backward (node->right, func);
(*func) (node->key, node->value);
avl_walk_backward (node->left, func);
}
}
void
avl_foreach (tree, func, direction)
avl_tree *tree;
void (*func) ();
int direction;
{
if (direction == AVL_FORWARD)
{
avl_walk_forward (tree->root, func);
}
else
{
avl_walk_backward (tree->root, func);
}
}
static void
free_entry (node, key_free, value_free)
avl_node *node;
void (*key_free) ();
void (*value_free) ();
{
if (node != NIL (avl_node))
{
free_entry (node->left, key_free, value_free);
free_entry (node->right, key_free, value_free);
if (key_free != 0)
(*key_free) (node->key);
if (value_free != 0)
(*value_free) (node->value);
FREE (node);
}
}
void
avl_free_table (tree, key_free, value_free)
avl_tree *tree;
void (*key_free) ();
void (*value_free) ();
{
free_entry (tree->root, key_free, value_free);
FREE (tree);
}
int
avl_count (tree)
avl_tree *tree;
{
return tree->num_entries;
}
static avl_node *
new_node (key, value)
char *key;
char *value;
{
register avl_node *new;
new = ALLOC (avl_node, 1);
new->key = key;
new->value = value;
new->height = 0;
new->left = new->right = NIL (avl_node);
return new;
}
int
avl_numcmp (x, y)
char *x, *y;
{
return (int) x - (int) y;
}
int
avl_check_tree (tree)
avl_tree *tree;
{
int error = 0;
(void) do_check_tree (tree->root, tree->compar, &error);
return error;
}
static int
do_check_tree (node, compar, error)
avl_node *node;
int (*compar) ();
int *error;
{
int l_height, r_height, comp_height, bal;
if (node == NIL (avl_node))
{
return -1;
}
r_height = do_check_tree (node->right, compar, error);
l_height = do_check_tree (node->left, compar, error);
comp_height = MAX (l_height, r_height) + 1;
bal = r_height - l_height;
if (comp_height != node->height)
{
(void) printf ("Bad height for 0x%08x: computed=%d stored=%d\n",
node, comp_height, node->height);
++*error;
}
if (bal > 1 || bal < -1)
{
(void) printf ("Out of balance at node 0x%08x, balance = %d\n",
node, bal);
++*error;
}
if (node->left != NIL (avl_node) &&
(*compar) (node->left->key, node->key) > 0)
{
(void) printf ("Bad ordering between 0x%08x and 0x%08x",
node, node->left);
++*error;
}
if (node->right != NIL (avl_node) &&
(*compar) (node->key, node->right->key) > 0)
{
(void) printf ("Bad ordering between 0x%08x and 0x%08x",
node, node->right);
++*error;
}
return comp_height;
}
ABC_NAMESPACE_IMPL_END