From: s1110238@giaeb.cc.monash.edu.au (Lee Hollingworth)
Subject: Re: AVL Source Code
Keywords: If you asked for AVL code read this!
Organization: Monash University, Melb., Australia.
Date: Thu, 30 Jul 1992 12:37:24 GMT

Sorry for using the net to post this but my e-mail responce to a few of
the requests for AVL source bounced.

So if you DO NOT want a copy of AVL tree code press 'n' now...

Lee Hollingworth
s1110238@giaeb.cc.monash.edu.au


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Lee Hollingworth <s1110238@giaeb> on Thu Jul 30 22:17:37 1992
#
# This archive contains:
#	avltree.c	avltree.doc	avltree.h	menu.c		
#	mscmake		tccmake		unixmake	
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - avltree.c
cat >avltree.c <<'@EOF'
/*
 * Name:    avltree
 * Purpose: To maintain a balanced binary tree using AVL algorithms
 * Files:   avltree.c  avltree.h
 * Author:  Lee Hollingworth
 * System:  ANSI C comformant
 * Date:    March 4 1992
 */

#include <stdio.h>
#include <stdlib.h>

#include "avltree.h"

/*
 * Name:    rotate_left
 * Purpose: To re-arrange node pointers resulting in a LL rotation
 * Passed:  tree:   address of pointer the rotation root
 * Returns: tree:   pointer to new root node
 */
void rotate_left(trees *tree)
{
    trees left = (*tree)->left;         /* left hand child */

    (*tree)->left = left->right;        /* re-arrange pointers */
    left->right = *tree;
    *tree = left;                   /* change root to top node of rotation */
}

/*
 * Name:    rotate_right
 * Purpose: To re-arrange node pointers resulting in a RR rotation
 * Passed:  tree:   address of pointer to the rotation node
 * Returns: tree:   pointer to new root node
 */
void rotate_right(trees *tree)
{
    trees right = (*tree)->right;       /* right hand child */

    (*tree)->right = right->left;       /* re-arrange pointers */
    right->left = *tree;
    *tree = right;                  /* change root to top node of rotation */
}

/*
 * Name:    rot_left_balance
 * Purpose: Balance the root node and it's right child after a LL rotation
 * Passed:  tree:   pointer to root node of rotation after the rotation has
 *                   taken place
 */
void rot_left_balance(trees tree)
{
    tree->balance = EVEN;
    if ((tree->right->left && tree->right->right) ||
            (!tree->right->left && !tree->right->right)) {
        tree->right->balance = EVEN;
    }
    else {
        tree->right->balance = tree->right->right ? R_HEAVY : L_HEAVY;
    }
}

/*
 * Name:    rot_right_balance
 * Purpose: Balance the root node and it's left child after a RR rotation
 * Passed:  tree:   pointer to root node of rotation after the rotation has
 *                   taken place
 */
void rot_right_balance(trees tree)
{
    tree->balance = EVEN;
    if ((tree->left->left && tree->left->right) ||
            (!tree->left->left && !tree->left->right)) {
        tree->left->balance = EVEN;
    }
    else {
        tree->left->balance = tree->left->right ? R_HEAVY : L_HEAVY;
    }
}

/*
 * Name:    left_balance
 * Purpose: Take care of left rotations, either LL or LR after a node has
 *           been inserted
 * Passed:  tree:   pointer to pointer to root of rotation point which has
 *                   violated the tree balance prior to any rotation taking
 *                   place
 *          key:    the new insertion value
 * Returns: tree:   new root node
 */
void left_balance(trees *tree, keys key)
{
    trees *child = &(*tree)->left;

    if (compare((*tree)->left->key,  key) > 0) {  /* LL rotation */
        rotate_left(tree);
        rot_left_balance(*tree);
    }
    else {                                  /* LR rotation */
        rotate_right(child);
        rot_right_balance(*child);
        rotate_left(tree);
        rot_left_balance(*tree);
    }
}

/*
 * Name:    right_balance
 * Purpose: Take care of right rotations, either RR or RL after a node has
 *           been inserted
 * Passed:  tree:   pointer to pointer to root of rotation point which has
 *                   violated the tree balance prior to any rotation taking
 *                   place
 *          key:    the new insertion value
 * Returns: tree:   new root node
 */
void right_balance(trees *tree, keys key)
{
    trees *child = &(*tree)->right;

    if (compare((*tree)->right->key, key) < 0) {   /* RR rotation */
        rotate_right(tree);
        rot_right_balance(*tree);
    }
    else {                                  /* RL rotation */
        rotate_left(child);
        rot_left_balance(*child);
        rotate_right(tree);
        rot_right_balance(*tree);
    }
}

/*
 * Name:    insert
 * Purpose: To search for and insert if necessary a key in a
 *           binary search tree.
 * Passed:  new_key: the key to be inserted
 *          tree:    the tree
 * Returns: tree:    possibly modified tree containing new_key
 *          TRUE     a new node was added so check the balance
 *          FALSE    balance is okay or node already in tree
 *           (maybe should return something else for duplicate key???)
 */
boolean insert(keys new_key, trees *tree)
{
    int result;

    if (*tree == NULL) {
        /*
         * at leaf, so key was not in the tree and must be added
         */
        *tree = malloc(sizeof(**tree));
        keycpy((*tree)->key, new_key);
        (*tree)->balance = EVEN;
        (*tree)->left = (*tree)->right = NULL;
        return TRUE;
    }
    else if ((result = compare(new_key, (*tree)->key)) < 0) {
        if (insert(new_key, &((*tree)->left))) {
            switch ((*tree)->balance) {
                case EVEN:
                    (*tree)->balance = L_HEAVY;
                    return TRUE;

                case R_HEAVY:
                    (*tree)->balance = EVEN;
                    return FALSE;                   /* tree is balanced */

                case L_HEAVY:
                    left_balance(tree, new_key);
                    return FALSE;                   /* tree is balanced */
            }

        }
        return FALSE;
    }
    else if (result > 0) {
        if (insert(new_key, &((*tree)->right))) {
            switch ((*tree)->balance) {
                case EVEN:
                    (*tree)->balance = R_HEAVY;
                    return TRUE;

                case L_HEAVY:
                    (*tree)->balance = EVEN;
                    return FALSE;                   /* tree is balanced */

                case R_HEAVY:
                    right_balance(tree, new_key);
                    return FALSE;                   /* tree is balanced */
            }
        }
        return FALSE;
    }
    else {
        /*
         * key already in tree so return FALSE
         */
        return FALSE;
    }
}

/*
 * Name:    del_ll
 * Purpose: Balance the tree after a deletion has taken place and a LL
 *           rotation is required
 * Passed:  tree:   pointer to pointer to root rotation node
 * Returns: tree:   pointer to pointer to new root node after rotation
 */
void del_ll(trees *tree)
{
    if ((*tree)->balance == L_HEAVY && (*tree)->left->balance == EVEN) {
        (*tree)->left->balance = R_HEAVY;
    }
    else {
        (*tree)->balance = EVEN;
        (*tree)->left->balance = R_HEAVY;
    }

    rotate_left(tree);
}

/*
 * Name:    del_rr
 * Purpose: Balance the tree after a deletion has taken place and a RR
 *           rotation is required
 * Passed:  tree:   pointer to pointer to root rotation node
 * Returns: tree:   pointer to pointer to new root node after rotation
 */
void del_rr(trees *tree)
{
    if ((*tree)->balance == R_HEAVY && (*tree)->right->balance == EVEN) {
        (*tree)->right->balance = L_HEAVY;
    }
    else {
        (*tree)->balance = EVEN;
        (*tree)->right->balance = L_HEAVY;
    }
    rotate_right(tree);
}

/*
 * Name:    del_lr
 * Purpose: Balance the tree after a deletion has taken place and a LR
 *           rotation is required
 * Passed:  tree:   pointer to pointer to root rotation node
 * Returns: tree:   pointer to pointer to new root node after rotation
 */
void del_lr(trees *tree)
{
    trees *child = &(*tree)->left;

    /*
     * (*tree)->balance == L_HEAVY in all cases
     * (*child)->balance == R_HEAVY in all cases
     */

    switch ((*child)->right->balance) {
        case R_HEAVY:
            (*child)->balance = L_HEAVY;
            (*child)->right->balance = EVEN;        /* new root */
            (*tree)->balance = EVEN;
            break;

        case EVEN:
            (*child)->balance = EVEN;
            (*tree)->balance = EVEN;
            break;

        case L_HEAVY:
            (*child)->balance = EVEN;
            (*tree)->balance = R_HEAVY;
            (*child)->right->balance = EVEN;
            break;
    }

    rotate_right(child);
    rotate_left(tree);
}

/*
 * Name:    del_rl
 * Purpose: Balance the tree after a deletion has taken place and a RL
 *           rotation is required
 * Passed:  tree:   pointer to pointer to root rotation node
 * Returns: tree:   pointer to pointer to new root node after rotation
 */
void del_rl(trees *tree)
{
    trees *child = &(*tree)->right;

    /*
     * (*tree)->balance == R_HEAVY in all cases
     * (*child)->balance == L_HEAVY in all cases
     */

    switch ((*child)->left->balance) {
        case L_HEAVY:
            (*child)->balance = R_HEAVY;
            (*child)->left->balance = EVEN;         /* new root */
            (*tree)->balance = EVEN;
            break;

        case EVEN:
            (*child)->balance = EVEN;
            (*tree)->balance = EVEN;
            break;

        case R_HEAVY:
            (*child)->balance = EVEN;
            (*tree)->balance = L_HEAVY;
            (*child)->left->balance = EVEN;
            break;
    }

    rotate_left(child);
    rotate_right(tree);
}

/*
 * Name:    left_path_balance
 * Purpose: If returning up the left path, check the current nodes balance
 *           and adjust if required
 * Passed:  pointer to pointer to the root node
 * Returns: FALSE no further balancing required above this point
 *          TRUE continue to balance up the tree
 */
boolean left_path_balance(trees *tree)
{
    switch ((*tree)->balance) {
        case EVEN:          /* no further balancing req'd */
            (*tree)->balance = R_HEAVY;
            return FALSE;

        case L_HEAVY:       /* check further up... */
            (*tree)->balance = EVEN;
            break;

        case R_HEAVY:       /* tree is unbalanced rotations req'd */
            /*
             * three possible cases now exist
             * 1. right child is EVEN
             * 2. right child is R_HEAVY
             * 3. right child is L_HEAVY
             * 1 = RR and change balances according to current
             *      balances, tree is now balanced
             * 2 = set both balances to EVEN and RR
             * 3 = RL and change balances according to current
             *      balances
             */
            switch ((*tree)->right->balance) {
                case EVEN:
                    del_rr(tree);
                    return FALSE;

                case R_HEAVY:
                    (*tree)->balance = EVEN;
                    (*tree)->right->balance = EVEN;
                    rotate_right(tree);
                    break;

                case L_HEAVY:
                    del_rl(tree);
                    break;
            }
    }
    return TRUE;
}

/*
 * Name:    right_path_balance
 * Purpose: If returning up the right path, check the current nodes balance
 *           and adjust if required
 * Passed:  pointer to pointer to the root node
 * Returns: FALSE no further balancing required above this point
 *          TRUE continue to balance up the tree
 */
boolean right_path_balance(trees *tree)
{
    switch ((*tree)->balance) {
        case EVEN:      /* no further balancing req'd */
            (*tree)->balance = L_HEAVY;
            return FALSE;

        case R_HEAVY:   /* continue to check on the way up */
            (*tree)->balance = EVEN;
            break;

        case L_HEAVY:
            /*
             * three possible cases now exist
             * 1. left child is EVEN
             * 2. left child is L_HEAVY
             * 3. left child is R_HEAVY
             * 1 = LL and change balances according to current
             *      balances, tree is now balanced
             * 2 = set both balances to EVEN and LL
             * 3 = LR and change balances according to current
             *      balances
             */
            switch ((*tree)->left->balance) {
                case EVEN:
                    del_ll(tree);
                    return FALSE;

                case L_HEAVY:
                    (*tree)->balance = EVEN;
                    (*tree)->left->balance = EVEN;
                    rotate_left(tree);
                    break;

                case R_HEAVY:
                    del_lr(tree);
                    break;
            }
    }
    return TRUE;
}

/*
 * Name:    left_most
 * Purpose: To retrieve the left most descendent of the tree,
 *            and delete this leaf node.
 * Passed:  tree: the tree to be searched
 * Returns: key:     the left most key in this tree
 *          tree:    the tree minus the leftmost node
 *          TRUE until point of balance is reached
 *          FALSE if no deletion or balance acheived
 */
boolean left_most(trees *tree, keys *key)
{
    trees p;               /* node to be disposed of */

    /*
     * first make sure there is something there to find
     */
    if ((*tree) != NULL) {
        /*
         * see if we are already at the bottom of the tree
         */
        if ((*tree)->left == NULL) {
            /*
             * return this left most node, and delete it, tree path is now
             * shorter so return TRUE
             */
            *key = (*tree)->key;
            p = (*tree);
            (*tree) = (*tree)->right;
            free(p);
            return TRUE;
        }
        else {
            /*
             * recurse down the left sub-tree, return of TRUE means a node
             * was removed from the left path so check balance of tree on
             * way up and adjust accordingly
             */
            if (left_most(&((*tree)->left), key)) {
                return left_path_balance(tree);
            }
            return FALSE;
        }
    }
    return FALSE;
}

/*
 * Name:    del_item
 * Purpose: To delete the item in the root of the tree.
 * Passed:  tree:    the tree whose root must be deleted
 * Returns: tree:    the tree with its root deleted
 *          TRUE until point of balance is reached
 *          FALSE if no deletion or balance acheived
 */
boolean del_item(trees *tree)
{
    trees p;                /* node to be disposed of */

    /*
     * first check that there is something to de deleted
     */
    if ((*tree) != NULL) {
        if (((*tree)->left == NULL) && ((*tree)->right == NULL)) {
            /*
             * if tree is a leaf, then it can be deleted easily, path
             *  has been shortened so return TRUE
             */
            free((*tree));
            (*tree) = NULL;
            return TRUE;
        }
        else if ((*tree)->left == NULL) {
            /*
             * if the tree only has a right child, then the root
             *  can be replaced by the right child
             * path has been shortened so return TRUE
             */
            p = (*tree);
            (*tree) = (*tree)->right;
            free(p);
            return TRUE;
        }
        else if ((*tree)->right == NULL) {
            /*
             * if the tree only has a left child, then the root
             *  can be replaced by the left child
             * path has been shortened so return TRUE
             */
            p = (*tree);
            (*tree) = (*tree)->left;
            free(p);
            return TRUE;
        }
        else {
            /*
             * if the tree has two children, then recurse to find
             *  the smallest key larger than the root (i.e. the
             *  left most key to the right of the root)
             * the path from where the replacement node has been
             *  taken is shortened, so the tree must be re-balanced
             *  accordingly
             */
            if (left_most(&((*tree)->right), &(*tree)->key)) {
                return right_path_balance(tree);
            }
            return FALSE;
        }
    }
}

/*
 * Name:    delete
 * Purpose: To delete an item from the tree.
 * Passed:  tree:    the tree to be deleted from
 *          key:     the key value of the item to be deleted
 * Returns: tree:    the tree with the item deleted
 *          TRUE  balancing required
 *          FALSE no further balancing required
 */
boolean delete(trees *tree, keys key)
{
    int result;

    if ((*tree) == NULL) {
        /*
         * key not in tree
         */
        return FALSE;
    }
    else if ((result = compare(key, (*tree)->key)) == 0) {
        /*
         * key found
         */
        return del_item(tree);
    }
    else if (result < 0) {
        /*
         * key could be in left sub-tree
         */
        if (delete(&((*tree)->left), key)) {
            return left_path_balance(tree);
        }
        return FALSE;
    }
    else {
        /*
         * key could be in right sub-tree
         */
        if (delete(&((*tree)->right), key)) {
            return right_path_balance(tree);
        }
        return FALSE;
    }
}

/*
 * Name:    find
 * Purpose: To search for a key in the binary search tree.
 * Passed:  key: the key to be found
 *          tree:    the tree
 * Returns: TRUE     the node was found
 *          FALSE    the node was not found
 */
boolean find(keys key, trees tree)
{
    int result;

    if (tree == NULL) {
        /*
         * at leaf, so key was not in the tree
         */
        return FALSE;
    }
    else if ((result = compare(key, tree->key)) < 0) {
        return (find(key, tree->left));
    }
    else if (result > 0) {
        return (find(key, tree->right));
    }
    else {
        /*
         * key found
         */
        return TRUE;
    }
}
@EOF

chmod 600 avltree.c

echo x - avltree.doc
cat >avltree.doc <<'@EOF'
AVLtree.doc -- AVL tree deletion                            Lee Hollingworth

The deletion of a node from an AVL balanced tree is far more complex than an
insertion, as can easily be confirmed by the way in which any text I could
find "left the implementation as an exercise to the reader"!

The problem of deletion can be reduced to the following steps:

    1. Reduce the problem to the point where the node to be deleted, say
       'p' has at most one child.  This of course was taken care of for
       us with the code supplied by me.  The code supplied uses the
       an in-order traversal to find the successor of p.
       For the rest of this explanation this child will be called 'q'.

    2. Delete the node 'q' from the tree.  As my comments state, it is
       known that 'q' can only have a right child by virtue of the method
       used to find it.  So deletion is a simple matter of linking the
       parent of 'q' to it's right child, which of course could be NULL.
       The height of the path from 'p' to 'q' has  now been reduced by 1,
       and the effects of this change in height must be traced back up the
       path from 'q' to the root of the tree.
       A boolean value of either TRUE or FALSE is used to show if the height
       of the current sub-tree has been shortened.  Using this value and
       the current node's balance and it's child's balance we can take the
       necessary action required in balancing the tree.

    3. Using the initial return value of TRUE, the following steps are
       undertaken, based upon the current value of each node 's' and the
       *return* path traveled to get to 's'.
       By returning FALSE the tree above that point remains unchanged.

    4. i.   The balance factor of 's' is EQUAL.  The balance factor of 's'
            is changed in accordance with whether the left or right path
            was shortened and the boolean value FALSE is passed on up the tree,
            thus terminating the need for any further changes.

       ii.  The balance of 's' is not EQUAL, but the taller sub-tree was
            shortened.  Change the balance of 's' to EQUAL and return TRUE.

       iii. The balance of 's' is not EQUAL, but the shorter sub-tree was
            shortened.
            This is a point in which a leaf in the sub-tree is 2 levels
            taller than it's matching leaf, thus violating AVL balance.
            Now a rotation is required to restore balance, but the rotation
            required depends upon the balance value of 's' and it's child on
            the taller sub-tree 't' (the path not shortened).

            a.  The balance value of 't' is EQUAL.  A single rotation is
                required, with appropriate balance changes (easily said),
                and FALSE is returned up the tree.

            b.  The balance value of 't' is the same as 's'. A single rotation
                is required, set 't' and 's' to EQUAL and return TRUE.

            c.  The balance of 's' and 't' are opposite.  A double rotation,
                first around 't' and then 's' is required, set the balance
                factor of the new root to EQUAL and the other as appropriate
                (very easily said).  Return TRUE.

       As usual the rotations required in 4(iii) depends upon which sub-tree
       was shortened, basically the opposite to an insertion.

The above is basically paraphrased from the book _Data Structures and Program
Design in C_  by Kruse, Leung and Tondo, Prentice Hall page 338.

Unfortunately they do not give a coded example!

The obvious problem with AVL tree node deletion is that of ensuring the correct
balance values is maintained within each node in the path from replacement node
to root.
To assist in this matter I have included a defined constant called SHOW_BALANCE
which if included at compile time will result in each node showing it's balance
value when printed.  This constant can be commented out to produce a normal
printed tree.

@EOF

chmod 600 avltree.doc

echo x - avltree.h
cat >avltree.h <<'@EOF'
/*
 * Name    : avltree
 * Purpose : Header file for avltree.c
 * Author  : Lee Hollingworth
 */

/*
 * macro   : compare
 * purpose : simple compare macro which is implemented here so that if the key
 *           field in the node structure is changed, the only place the code
 *           needs to be changed is here...
 * returns :  0 - a and b are equal
 *            1 - a is greater than b
 *           -1 - b is greater than a
 *
 * note compatible with strcmp
 */
#define compare(a, b)  (a) == (b) ? 0 : (a) > (b) ? 1 : -1

/*
 * macro   : keycpy
 * purpose : copy one key into another
 * note    : can be replaced by any copy function such as strcpy for string
 *           keys
 */
#define keycpy(a, b) a = b

/*
 * datatype: balances
 * purpose : mainatin a balance field in the current node
 */
typedef enum {EVEN, L_HEAVY, R_HEAVY} balances;

/*
 * datatype: boolean
 * purpose : TRUE - FALSE data type
 */
typedef enum {FALSE, TRUE} boolean;

/*
 * datatype: keys
 * purpose : define a key field data type
 */
typedef int keys;

/*
 * datatype: node
 * purpose : tree node structure
 */
typedef struct node {
    keys key;                   /* search key */
    balances balance;           /* level of balance of children */
    struct node *left;          /* left sub-tree */
    struct node *right;         /* right sub-tree */
};

/*
 * datatype: nodes
 * purpose : define node type
 */
typedef struct node *trees;     /* every tree or sub-tree */

/*
 * functions "interfaced" to other source code
 */
boolean  insert(keys new_key,trees *tree);      /* insert new node */
boolean  delete(trees *tree,keys key);          /* delete a node */
boolean  find(keys key, trees tree);            /* find a node */

/*
 * functions "private" to avltree.c
 */
void  rotate_left(trees *tree);
void  rotate_right(trees *tree);
void  rot_left_balance(trees tree);
void  rot_right_balance(trees tree);
void  left_balance(trees *tree, keys key);
void  right_balance(trees *tree, keys key);
void  del_ll(trees *tree);
void  del_rr(trees *tree);
void  del_lr(trees *tree);
void  del_rl(trees *tree);
boolean  left_path_balance(trees *tree);
boolean  right_path_balance(trees *tree);
boolean  left_most(trees *tree, keys *key);
boolean  del_item(trees *tree);
@EOF

chmod 600 avltree.h

echo x - menu.c
cat >menu.c <<'@EOF'
/*
 * Name:    Balanced binary search with AVL balancing
 * Purpose: To insert and delete keys into a binary tree and display the
 *           result using AVL algorithms to maintain a balanced tree.
 * File:    avltree.c
 * Author:  Lee Hollingworth
 * System:  ANSI C comformant
 * Date:    March 4 1992
 */

#include <stdio.h>
#include <stdlib.h>

#include "avltree.h"

/*
 * SHOW_BALANCE allows the tree node balances to be shown for easy viewing
 *  of tree re-balancing after deletions or insertions
 *  to use it just remove comments markers
 */


#define SHOW_BALANCE


typedef enum {ADD, DELETE, FIND, QUIT} options;

/*
 * Name:    print_tree
 * Purpose: To display the entire tree in a reasonable format.
 * Passed:  tree:  the tree to be displayed
 *          level: how far down from the root of the tree we are
 * Output:  visual representation of the tree
 */
void print_tree(trees tree, int level)
{
    int i;

    /*
     * recursion ceases when we reach the bottom of the tree
     */
    if (tree != NULL) {
       /*
        * Still more tree to print, so print the right sub-tree,
        *  then this node, then the left sub-tree.
        * Indentation is proportional to the depth in the tree.
        */
        print_tree(tree->right, level+1);
        for (i=0; i < level; i++)
            printf("       ");
        printf("%d", tree->key);

        #ifdef SHOW_BALANCE
        printf("%c", tree->balance == 0 ? 'E' : tree->balance == 1 ? 'L' : 'R');
        #endif

        printf("\n");
        print_tree(tree->left, level+1);
    }
}

/*
 * Name:    menu
 * Purpose: To display a menu and input the user's selection.
 * Returns: selection
 */
int menu(void)
{
    int c;

    for (;;) {
        printf("Add, Delete, Find or Quit? (a/d/f/q): ");
        while ((c = tolower(getchar())) == '\n')
            ;
        switch (c) {
        case 'a':
            return ADD;
        case 'd':
            return DELETE;
        case 'f':
            return FIND;
        case 'q':
            return QUIT;
        }
    }
}

/*
 * Name:    main
 * Purpose: See comments at start of program.
 */
void main(void)
{
    trees root;                 /* the entire tree */
    keys key;                   /* the key being added */

    root = NULL;   /* start with an empty tree */

    /*
     * Keep on inputting a new key, adding or deleting this key,
     *  and displaying the resultant tree, until the user wants
     *  to stop.
     */
    for (;;) {
        switch (menu()) {
        case ADD:
            printf("Key to add: ");
            scanf("%d", &key);
            insert(key, &root);
            break;
        case DELETE:
            printf("Key to delete: ");
            scanf("%d", &key);
            delete(&root, key);
            break;
        case FIND:
            printf("Key to find: ");
            scanf("%d", &key);
            if (find(key, root)) {
                printf("%d: Key found!\n", key);
            }
            else {
                printf("%d: Key not in tree...\n", key);
            }
            break;
        case QUIT:
            exit(0);
        }
        print_tree(root, 0);
    }
}
@EOF

chmod 600 menu.c


rm -f /tmp/uud$$
(echo "begin 666 /tmp/uud$$\n#;VL*n#6%@x\n \nend" | uudecode) >/dev/null 2>&1
if [ X"`cat /tmp/uud$$ 2>&1`" = Xok ]
then
	unpacker=uudecode
else
	echo Compiling unpacker for non-ascii files
	pwd=`pwd`; cd /tmp
	cat >unpack$$.c <<'EOF'
#include <stdio.h>
#define C (*p++ - ' ' & 077)
main()
{
	int n;
	char buf[128], *p, a,b;

	scanf("begin %o ", &n);
	gets(buf);

	if (freopen(buf, "w", stdout) == NULL) {
		perror(buf);
		exit(1);
	}

	while (gets(p=buf) && (n=C)) {
		while (n>0) {
			a = C;
			if (n-- > 0) putchar(a << 2 | (b=C) >> 4);
			if (n-- > 0) putchar(b << 4 | (a=C) >> 2);
			if (n-- > 0) putchar(a << 6 | C);
		}
	}
	exit(0);
}
EOF
	cc -o unpack$$ unpack$$.c
	rm unpack$$.c
	cd $pwd
	unpacker=/tmp/unpack$$
fi
rm -f /tmp/uud$$

echo x - mscmake '[non-ascii]'
$unpacker <<'@eof'
begin 600 mscmake
M(R!-86ME($%63"Y%6$4@=&\@<G5N(&]N(&UO<W0@24)-(%!#('-Y<W1E;7,NX
M"B,@5&AI<R!F:6QE(&ES(&9O<B!-:6-R;W-O9G0@0R V+C @;W(@475I8VL@X
M0R R+C4Q"B,@268@=7-I;F<@34%+12 H;F]T($Y-04M%*2!C;VUM96YT(&]UX
M="!N97AT(&QI;F4N"D%,3" Z(&%V;"YE>&4*"BYC+F]B:CH*(" @(&-L("]CX
M("]!4R O5S0@+T<R("]*("0\"@IM96YU+F]B:CH@879L=')E92YH(&UE;G4NX
M8PIA=FQT<F5E+F]B:CH@879L=')E92YH(&%V;'1R964N8PH*879L+F5X93H@X
M;65N=2YO8FH@879L=')E92YO8FH*(" @3$E.2R!M96YU(&%V;'1R964L(&%VX
*;"YE>&4["B @('9L                                            X
                                                             X
end
@eof

chmod 600 mscmake

echo x - tccmake
sed 's/^@//' >tccmake <<'@EOF'
#    Turbo C++ Make file for AVL tree program.

#    Implicit rules.

@.c.obj:
    tcc -c $<

#    Targets.

avl.exe:        menu.obj avltree.obj
        tcc -G -O -Z -w -eavl.exe menu.obj avltree.obj

#    Dependencies.

menu.obj:      menu.c avltree.h
avltree.obj:   avltree.c avltree.h
@EOF

chmod 600 tccmake

echo x - unixmake
sed 's/^@//' >unixmake <<'@EOF'
# Make avltree for UNIX [System V] systems

OBJECTS = menu.o avltree.o

# -z - check for dereferencing NULL pointer
CCFLAGS = -O -z +DA1.0

@.c.o:           ; cc $(CCFLAGS) -c -DUNIX $*.c

avltree: $(OBJECTS) ; cc $(CCFLAGS) -o avltree $(OBJECTS) -lcurses

$(OBJECTS):     avltree.h
menu.o:
avltree.o:
@EOF

chmod 644 unixmake

rm -f /tmp/unpack$$
exit 0