File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / util / gtree.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 4 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * Copyright (c) 2001-2002 Packet Design, LLC.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty,
 * use and redistribution of this software, in source or object code
 * forms, with or without modifications are expressly permitted by
 * Packet Design; provided, however, that:
 * 
 *    (i)  Any and all reproductions of the source or object code
 *         must include the copyright notice above and the following
 *         disclaimer of warranties; and
 *    (ii) No rights are granted, in any manner or form, to use
 *         Packet Design trademarks, including the mark "PACKET DESIGN"
 *         on advertising, endorsements, or otherwise except as such
 *         appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
 * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
 * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
 * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
 * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
 * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
 * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
 * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
 * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Archie Cobbs <archie@freebsd.org>
 */

#include <sys/types.h>
#include <sys/param.h>

#ifdef _KERNEL
#include <sys/systm.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#endif	/* !_KERNEL */

#include "structs/structs.h"
#include "structs/type/array.h"

#include "util/gtree.h"
#include "util/typed_mem.h"

/* Enabled debug tracing: only use this when keys are strings */
#define GTREE_TRACE	0

enum node_color {
	BLACK=0,
	RED=1
};

/* One node in the tree */
struct node {
	enum node_color	color;			/* item color */
	struct node	*left;			/* left child */
	struct node	*right;			/* right child */
	struct node	*parent;		/* parent */
	const void	*item;			/* this item */
};

/* The tree itself */
struct gtree {
	void		*arg;
	gtree_cmp_t	*cmp;
	gtree_add_t	*add;
	gtree_del_t	*del;
	gtree_print_t	*print;
	u_int		mods;			/* modification counter */
	u_int		size;			/* number of items in table */
	struct node	*root;			/* root of tree */
	const char	*mtype;			/* memory type */
	char		mtbuf[TYPED_MEM_TYPELEN];
	u_char		locked;
};

/*
 * Internal functions
 */
static struct	node *gtree_find(struct gtree *g,
			const void *item, struct node **parentp);
static struct	node *gtree_get_next(struct gtree *g, struct node *node);
static struct	node *gtree_get_prev(struct gtree *g, struct node *node);
static void	gtree_insert_fixup(struct gtree *g, struct node *x);
static void	gtree_delete_fixup(struct gtree *g, struct node *x);
static void	gtree_rotate_left(struct gtree *g, struct node *x);
static void	gtree_rotate_right(struct gtree *g, struct node *x);
#ifndef _KERNEL
static void	gtree_print_node(struct gtree *g, FILE *fp,
			struct node *node, const char *which, int depth);
#endif

static gtree_cmp_t	gtree_default_cmp;
static gtree_add_t	gtree_default_add;
static gtree_del_t	gtree_default_del;

/*
 * Internal variables
 */

#define NIL	(&nil)

/* Note: nil->parent is modified during normal operation */
static struct	node nil = { BLACK, NIL, NIL, NULL };

/*
 * Create a new tree.
 */
struct gtree *
gtree_create(void *arg, const char *mtype, gtree_cmp_t *cmp,
	gtree_add_t *add, gtree_del_t *del, gtree_print_t *print)
{
	struct gtree *g;

	/* Create new tree object */
	if ((g = MALLOC(mtype, sizeof(*g))) == NULL)
		return (NULL);
	memset(g, 0, sizeof(*g));
	g->arg = arg;
	g->cmp = cmp != NULL ? cmp : gtree_default_cmp;
	g->add = add != NULL ? add : gtree_default_add;
	g->del = del != NULL ? del: gtree_default_del;
	g->print = print;
	g->root = NIL;

	/* Create memory subtypes */
	if (mtype != NULL) {
		strlcpy(g->mtbuf, mtype, sizeof(g->mtbuf));
		g->mtype = g->mtbuf;
	}

#if GTREE_TRACE
	/* Tracing */
	if (g->print != NULL)
		fprintf(stderr, "%s[%p]: tree created\n", __FUNCTION__, g);
#endif

	/* Done */
	return (g);
}

/*
 * Destroy a tree.
 */
void
gtree_destroy(struct gtree **gp)
{
	struct gtree *const g = *gp;

	/* Sanity check */
	if (g == NULL)
		return;
	assert(!g->locked);

	/* Remove all remaining nodes */
	while (g->root != NIL)
		gtree_remove(g, g->root->item);

#if GTREE_TRACE
	/* Tracing */
	if (g->print != NULL)
		fprintf(stderr, "%s[%p]: tree destroyed\n", __FUNCTION__, g);
#endif

	/* Free object */
	FREE(g->mtype, g);
	*gp = NULL;
}

/*
 * Get the argument supplied to gtree_create().
 */
void *
gtree_arg(struct gtree *g)
{
	return (g->arg);
}

/*
 * Return number of items in the table.
 */
u_int
gtree_size(struct gtree *g)
{
	return (g->size);
}

/*
 * Get an item.
 *
 * Returns the item, or NULL if the item does not exist.
 */
void *
gtree_get(struct gtree *g, const void *item)
{
	struct node *dummy;
	struct node *node;

	/* Find node */
	node = gtree_find(g, item, &dummy);

#if GTREE_TRACE
	/* Tracing */
	if (g->print != NULL) {
		fprintf(stderr, "%s[%p]: key=\"%s\" found=%d size=%u\n",
		    __FUNCTION__, g, (*g->print)(g, (void *)item),
		    node != NULL, g->size);
	}
#endif

	/* Return result */
	if (node != NULL)
		return ((void *)node->item);
	return (NULL);
}

/*
 * Put an item.
 *
 * Returns 0 if the item is new, 1 if it replaces an existing
 * item, and -1 if there was an error.
 */
int
gtree_put(struct gtree *g, const void *item)
{
	return (gtree_put_prealloc(g, item, NULL));
}

/*
 * Put an item -- common internal code.
 */
int
gtree_put_prealloc(struct gtree *g, const void *item, void *new_node)
{
	struct node *parent;
	struct node *node;

	/* NULL item is invalid */
	if (item == NULL) {
		errno = EINVAL;
		return (-1);
	}

	/* Lock tree */
	if (g->locked) {
		errno = EBUSY;
		return (-1);
	}
	g->locked = 1;

	/* See if item already exists */
	node = gtree_find(g, item, &parent);

#if GTREE_TRACE
	/* Tracing */
	if (g->print != NULL) {
		fprintf(stderr, "%s[%p]: key=\"%s\" exists=%d newsize=%u\n",
		    __FUNCTION__, g, (*g->print)(g, (void *)item),
		    node != NULL, g->size + (node == NULL));
	}
#endif

	/* If item already exists, just replace */
	if (node != NULL) {
		if (new_node != NULL)
			FREE(g->mtype, new_node);	/* didn't need it */
		g->mods++;
		(*g->del)(g, (void *)node->item);
		node->item = item;
		(*g->add)(g, (void *)node->item);
		g->locked = 0;
		return (1);
	}

	/* Allocate or use caller-supplied memory for new node */
	if (new_node != NULL)
		node = new_node;
	else if ((node = MALLOC(g->mtype, sizeof(*node))) == NULL) {
		g->locked = 0;
		return (-1);
	}
	memset(node, 0, sizeof(*node));
	node->color = RED;
	node->left = NIL;
	node->right = NIL;
	node->parent = parent;
	node->item = item;
	g->mods++;
	g->size++;

	/* Add to tree */
	if (parent != NULL) {
		if ((*g->cmp)(g, node->item, parent->item) < 0)
			parent->left = node;
		else
			parent->right = node;
	} else
		g->root = node;

	/* Rebalance */
	gtree_insert_fixup(g, node);

	/* Notify caller of new item */
	(*g->add)(g, (void *)node->item);

#if GTREE_TRACE
	/* Tracing */
	if (g->print != NULL)
		gtree_print(g, stderr);
#endif

	/* Unlock tree and return */
	g->locked = 0;
	return (0);
}

/*
 * Remove an item.
 *
 * Returns 1 if the item was found and removed, 0 if not found.
 */
int
gtree_remove(struct gtree *g, const void *item)
{
	struct node *dummy;
	struct node *node;
	struct node *x;
	struct node *y;

	/* Lock tree */
	if (g->locked) {
		errno = EBUSY;
		return (-1);
	}
	g->locked = 1;

	/* Find node */
	node = gtree_find(g, item, &dummy);

#if GTREE_TRACE
	/* Tracing */
	if (g->print != NULL) {
		fprintf(stderr, "%s[%p]: key=\"%s\" exists=%d newsize=%u\n",
		    __FUNCTION__, g, (*g->print)(g, (void *)item),
		    node != NULL, g->size - (node != NULL));
	}
#endif

	/* If not found, nothing to do */
	if (node == NULL) {
		g->locked = 0;
		return (0);
	}

	/* Notify caller of deletion */
	(*g->del)(g, (void *)node->item);

	/* Set y to node or first successor with a NIL child */
	if (node->left == NIL || node->right == NIL)
		y = node;
	else
		for (y = node->right; y->left != NIL; y = y->left);

	/* Set x to y's only child, or NIL if no children */
	if (y->left != NIL)
		x = y->left;
	else
		x = y->right;

	/* Remove y from the parent chain */
	x->parent = y->parent;
	if (y->parent != NULL) {
		if (y == y->parent->left)
			y->parent->left = x;
		else
			y->parent->right = x;
	} else
		g->root = x;

	/* Move y's item to node */
	if (y != node)
		node->item = y->item;

	/* Do fixup if necessary */
	if (y->color == BLACK)
		gtree_delete_fixup(g, x);

	/* Free node y */
	FREE(g->mtype, y);

	/* Update stats */
	g->mods++;
	g->size--;

#if GTREE_TRACE
	/* Tracing */
	if (g->print != NULL)
		gtree_print(g, stderr);
#endif

	/* Unlock tree and return */
	g->locked = 0;
	return (1);
}

/*
 * Get the size of a node.
 */
u_int
gtree_node_size(void)
{
	return (sizeof(struct node));
}

/*
 * Get the first item.
 */
void *
gtree_first(struct gtree *g)
{
	struct node *node;

	if ((node = gtree_get_next(g, NULL)) == NULL)
		return (NULL);
	return ((void *)node->item);
}

/*
 * Get the next item.
 */
void *
gtree_next(struct gtree *g, const void *item)
{
	struct node *dummy;
	struct node *node;

	if ((node = gtree_find(g, item, &dummy)) == NULL)
		return (NULL);
	if ((node = gtree_get_next(g, node)) == NULL)
		return (NULL);
	return ((void *)node->item);
}

/*
 * Get the last item.
 */
void *
gtree_last(struct gtree *g)
{
	struct node *node;

	if ((node = gtree_get_prev(g, NULL)) == NULL)
		return (NULL);
	return ((void *)node->item);
}

/*
 * Get the previous item.
 */
void *
gtree_prev(struct gtree *g, const void *item)
{
	struct node *dummy;
	struct node *node;

	if ((node = gtree_find(g, item, &dummy)) == NULL)
		return (NULL);
	if ((node = gtree_get_prev(g, node)) == NULL)
		return (NULL);
	return ((void *)node->item);
}

/*
 * Traverse the tree.
 */
int
gtree_traverse(struct gtree *g, int (*handler)(struct gtree *g, void *item))
{
	struct node *node;
	int r = 0;

	/* Lock tree */
	if (g->locked) {
		errno = EBUSY;
		return (-1);
	}
	g->locked = 1;

	/* Get items in a list */
	for (node = gtree_get_next(g, NULL);
	    node != NULL; node = gtree_get_next(g, node)) {
		if ((*handler)(g, (void *)node->item) == -1) {
			r = -1;
			break;
		}
	}

	/* Done */
	g->locked = 0;
	return (r);
}

/*
 * Get a sorted array of tree contents.
 */
int
gtree_dump(struct gtree *g, void ***listp, const char *mtype)
{
	struct node *node;
	void **list;
	int i;

	/* Get items in a list */
	if ((list = MALLOC(mtype, g->size * sizeof(*list))) == NULL)
		return (-1);
	for (i = 0, node = gtree_get_next(g, NULL);
	    node != NULL; node = gtree_get_next(g, node))
		list[i++] = (void *)node->item;
	assert(i == g->size);

	/* Done */
	*listp = list;
	return (i);
}

#ifndef _KERNEL
/*
 * Print out a tree
 */
void
gtree_print(struct gtree *g, FILE *fp)
{
	gtree_print_node(g, fp, g->root, "ROOT", 0);
}
#endif

/**********************************************************************
			DEFAULT CALLBACKS
**********************************************************************/

static int
gtree_default_cmp(struct gtree *g, const void *item1, const void *item2)
{
	if (item1 < item2)
		return (-1);
	if (item1 > item2)
		return (-1);
	return (0);
}

static void
gtree_default_add(struct gtree *g, void *item)
{
	return;
}

static void
gtree_default_del(struct gtree *g, void *item)
{
	return;
}

/**********************************************************************
			HELPER METHODS
**********************************************************************/

/*
 * Find an item. If not found, set *parentp to the would-be parent
 * (NULL if the tree is empty).
 */
static struct node *
gtree_find(struct gtree *g, const void *item, struct node **parentp)
{
	struct node *node;
	int diff;

	*parentp = NULL;
	for (node = g->root; node != NIL; ) {
		diff = (*g->cmp)(g, item, node->item);
		if (diff == 0)
			return (node);
		*parentp = node;
		node = (diff < 0) ? node->left : node->right;
	}
	return (NULL);
}

/*
 * Get the next item in the tree after "node", or the first
 * item if "node" is NULL.
 */
static struct node *
gtree_get_next(struct gtree *g, struct node *node)
{
	if (node == NULL) {
		if (g->root == NIL)
			return (NULL);
		node = g->root;
	} else if (node->right != NIL)
		node = node->right;
	else {
		while (1) {
			if (node->parent == NULL
			    || node == node->parent->left)
				return (node->parent);
			node = node->parent;
		}
	}
	while (node->left != NIL)
		node = node->left;
	return (node);
}

/*
 * Get the previous item in the tree before "node", or the last
 * item if "node" is NULL.
 */
static struct node *
gtree_get_prev(struct gtree *g, struct node *node)
{
	if (node == NULL) {
		if (g->root == NIL)
			return (NULL);
		node = g->root;
	} else if (node->left != NIL)
		node = node->left;
	else {
		while (1) {
			if (node->parent == NULL
			    || node == node->parent->right)
				return (node->parent);
			node = node->parent;
		}
	}
	while (node->right != NIL)
		node = node->right;
	return (node);
}

/*
 * Rotate node x to the left
 */
static void
gtree_rotate_left(struct gtree *g, struct node *x)
{
	struct node *y = x->right;

	x->right = y->left;
	if (y->left != NIL)
		y->left->parent = x;
	if (y != NIL)
		y->parent = x->parent;
	if (x->parent != NULL) {
		if (x == x->parent->left)
			x->parent->left = y;
		else
			x->parent->right = y;
	} else
		g->root = y;
	y->left = x;
	if (x != NIL)
		x->parent = y;
}

/*
 * Rotate node x to the right
 */
static void
gtree_rotate_right(struct gtree *g, struct node *x)
{
	struct node *y = x->left;

	x->left = y->right;
	if (y->right != NIL)
		y->right->parent = x;
	if (y != NIL)
		y->parent = x->parent;
	if (x->parent != NULL) {
		if (x == x->parent->right)
			x->parent->right = y;
		else
			x->parent->left = y;
	} else
		g->root = y;
	y->right = x;
	if (x != NIL)
		x->parent = y;
}

/*
 * Reestablish red/black balance after inserting "node"
 */
static void
gtree_insert_fixup(struct gtree *g, struct node *x)
{
	while (x != g->root && x->parent->color == RED) {
		if (x->parent == x->parent->parent->left) {
			struct node *y = x->parent->parent->right;

			if (y->color == RED) {
				x->parent->color = BLACK;
				y->color = BLACK;
				x->parent->parent->color = RED;
				x = x->parent->parent;
			} else {
				if (x == x->parent->right) {
					x = x->parent;
					gtree_rotate_left(g, x);
				}
				x->parent->color = BLACK;
				x->parent->parent->color = RED;
				gtree_rotate_right(g, x->parent->parent);
			}
		} else {
			struct node *y = x->parent->parent->left;

			if (y->color == RED) {
				x->parent->color = BLACK;
				y->color = BLACK;
				x->parent->parent->color = RED;
				x = x->parent->parent;
			} else {
				if (x == x->parent->left) {
					x = x->parent;
					gtree_rotate_right(g, x);
				}
				x->parent->color = BLACK;
				x->parent->parent->color = RED;
				gtree_rotate_left(g, x->parent->parent);
			}
		}
	}
	g->root->color = BLACK;
}

/*
 * Reestablish red/black balance after deleting node x
 */
static void
gtree_delete_fixup(struct gtree *g, struct node *x)
{
	while (x != g->root && x->color == BLACK) {
		if (x == x->parent->left) {
			struct node *w = x->parent->right;

			if (w->color == RED) {
				w->color = BLACK;
				x->parent->color = RED;
				gtree_rotate_left(g, x->parent);
				w = x->parent->right;
			}
			if (w->left->color == BLACK
			    && w->right->color == BLACK) {
				w->color = RED;
				x = x->parent;
			} else {
				if (w->right->color == BLACK) {
					w->left->color = BLACK;
					w->color = RED;
					gtree_rotate_right(g, w);
					w = x->parent->right;
				}
				w->color = x->parent->color;
				x->parent->color = BLACK;
				w->right->color = BLACK;
				gtree_rotate_left(g, x->parent);
				x = g->root;
			}
		} else {
			struct node *w = x->parent->left;

			if (w->color == RED) {
				w->color = BLACK;
				x->parent->color = RED;
				gtree_rotate_right(g, x->parent);
				w = x->parent->left;
			}
			if (w->right->color == BLACK
			    && w->left->color == BLACK) {
				w->color = RED;
				x = x->parent;
			} else {
				if (w->left->color == BLACK) {
					w->right->color = BLACK;
					w->color = RED;
					gtree_rotate_left(g, w);
					w = x->parent->left;
				}
				w->color = x->parent->color;
				x->parent->color = BLACK;
				w->left->color = BLACK;
				gtree_rotate_right(g, x->parent);
				x = g->root;
			}
		}
	}
	x->color = BLACK;
}

#ifndef _KERNEL
/*
 * Recursively print out one node and all its descendants.
 */
static void
gtree_print_node(struct gtree *g, FILE *fp, struct node *node,
	const char *which, int depth)
{
	int i;

	for (i = 0; i < depth; i++)
		fprintf(fp, "  ");
	fprintf(fp, "%s: ", which);
	if (node == NIL) {
		fprintf(fp, "NIL\n");
		return;
	}
	if (g->print != NULL)
		fprintf(fp, "%s", (*g->print)(g, (void *)node->item));
	else
		fprintf(fp, "%p", node);
	fprintf(fp, " %s\n", node->color == RED ? "RED" : "BLACK");
	if (node->left != NIL)
		gtree_print_node(g, fp, node->left, "LEFT", depth + 1);
	if (node->right != NIL)
		gtree_print_node(g, fp, node->right, "RIGHT", depth + 1);
}
#endif	/* !_KERNEL */

#ifdef GTREE_TEST

/**********************************************************************
			TEST ROUTINE
**********************************************************************/

#include <err.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>

/* Debugging */
#define DBG(fmt, args...)				\
	do {						\
		if (debug)				\
			printf(fmt "\n" , ## args);	\
	} while (0)

static gtree_cmp_t	gtree_test_cmp;
static gtree_add_t	gtree_test_add;
static gtree_del_t	gtree_test_del;
static gtree_print_t	gtree_test_print;

static int	*trin;	/* trin[i] iff i in the tree, according to add, del */
static int	trsz;	/* size of tree according to add, del */
static int	*nums;	/* fixed array where nums[i] == i */
static u_long	num;	/* size of nums[] array */
static int	debug;
static struct	gtree *g;

static void	gtree_assert(int i, const char *s, const char *func, int line);
static int	chknode(struct node *node);

#define CHECK(x)	gtree_assert(x, #x, __FUNCTION__, __LINE__)

int
main(int ac, char **av)
{
	u_long count;
	u_long seed;
	int size = 0;		/* my idea of what the tree size should be */
	int *myin;		/* our idea of what values are in the tree */
	int ch;
	int i;

	/* Default seed and count */
	seed = htonl(getpid()) ^ time(NULL);
	count = 10000;

	/* Parse command line */
	while ((ch = getopt(ac, av, "dc:s:n:")) != -1) {
		switch (ch) {
		case 'c':
			count = strtoul(optarg, NULL, 0);
			break;
		case 'd':
			debug++;
			break;
		case 'n':
			num = strtoul(optarg, NULL, 0);
			break;
		case 's':
			seed = strtoul(optarg, NULL, 0);
			break;
		default:
		usage:
			errx(1, "usage: gtree [-s seed] [-c count] [-n num]");
		}
	}
	ac -= optind;
	av += optind;
	if (ac != 0)
		goto usage;

	/* Seed RNG */
	srandom(seed);
	if (num == 0)
		num = (random() % 500) + 1;
	printf("seed = %lu num = %lu count = %lu\n", seed, num, count);

	/* Initialize number array */
	if ((nums = MALLOC(TYPED_MEM_TEMP, num * sizeof(*nums))) == NULL)
		err(1, "malloc");
	if ((myin = MALLOC(TYPED_MEM_TEMP, num * sizeof(*myin))) == NULL)
		err(1, "malloc");
	if ((trin = MALLOC(TYPED_MEM_TEMP, num * sizeof(*trin))) == NULL)
		err(1, "malloc");
	for (i = 0; i < num; i++)
		nums[i] = i;
	memset(myin, 0, num * sizeof(*myin));
	memset(trin, 0, num * sizeof(*trin));

	/* Get tree */
	if ((g = gtree_create(NULL, "gtree", gtree_test_cmp,
	    gtree_test_add, gtree_test_del, gtree_test_print)) == NULL)
		err(1, "gtree_create");

	/* Iterate */
	for (i = 0; i < count; i++) {
		void **list;
		int first;
		int last;
		int add;
		int *p;
		int i;
		int r;

		/* Pick a random number and action */
		i = random() % num;
		add = (random() & (1 << (i % 17))) != 0;
		DBG("%s %u siz=%u in=%d",
		    add ? "add" : "del", i, size, myin[i]);

		/* Do an operation (add or remove) */
		if (add) {
			if (gtree_put(g, &nums[i]) == -1) {
				warn("gtree_put");
				CHECK(0);
			}
			if (!myin[i])
				size++;
			myin[i] = 1;
		} else {
			r = gtree_remove(g, &nums[i]);
			if (r == -1) {
				warn("gtree_remove");
				CHECK(0);
			}
			CHECK(r == myin[i]);
			if (myin[i])
				size--;
			myin[i] = 0;
		}

		/* Dump tree */
		if (debug >= 2)
			gtree_print(g, stdout);

		/* Check sizes */
		CHECK(trsz == size);
		CHECK(gtree_size(g) == size);

		/* Check presence of items */
		if (memcmp(trin, myin, num * sizeof(*trin)) != 0)
			CHECK(0);

		/* Check first and last */
		p = (int *)gtree_first(g);
		first = p ? *p : -1;
		p = (int *)gtree_last(g);
		last = p ? *p : -1;
		for (i = 0; i < num && myin[i] == 0; i++);
		if (i == num)
			i = -1;
		CHECK(first == i);

		/* Check last */
		for (i = num - 1; i >= 0 && myin[i] == 0; i--);
		CHECK(last == i);

		/* Check sorting */
		if ((r = gtree_dump(g, &list, TYPED_MEM_TEMP)) == -1)
			err(1, "gtree_dump");
		CHECK(r == size);
		for (i = 1; i < r; i++) {
			const int this = *((int *)list[i]);
			const int prev = *((int *)list[i - 1]);

			CHECK(prev < this);
		}
		FREE(TYPED_MEM_TEMP, list);

		/* Check red-black property */
		chknode(g->root);
	}

	/* Done */
	gtree_destroy(&g);
	FREE(TYPED_MEM_TEMP, nums);
	FREE(TYPED_MEM_TEMP, myin);
	FREE(TYPED_MEM_TEMP, trin);
	if (debug)
		typed_mem_dump(stdout);
	return (0);
}

static int
gtree_test_cmp(struct gtree *g, const void *item1, const void *item2)
{
	const int *const p1 = item1;
	const int *const p2 = item2;

	CHECK(p1 >= &nums[0] && p1 < &nums[num]);
	CHECK(p2 >= &nums[0] && p2 < &nums[num]);
	return (*((int *)item1) - *((int *)item2));
}

static void
gtree_test_add(struct gtree *g, void *item)
{
	const int *const p = item;
	const int i = *((int *)item);

	DBG("  add->%u", i);
	CHECK(p >= &nums[0] && p < &nums[num]);
	if (gtree_remove(g, item) != -1 || errno != EBUSY)
		CHECK(0);
	CHECK(!trin[i]);
	trin[i] = 1;
	trsz++;
}

static void
gtree_test_del(struct gtree *g, void *item)
{
	const int *const p = item;
	const int i = *((int *)item);

	DBG("  del->%u", i);
	CHECK(p >= &nums[0] && p < &nums[num]);
	if (gtree_put(g, item) != -1 || errno != EBUSY)
		CHECK(0);
	CHECK(trin[i]);
	trin[i] = 0;
	trsz--;
}

static const char *
gtree_test_print(struct gtree *g, const void *item)
{
	static char buf[16];

	snprintf(buf, sizeof(buf), "%03u", *((u_int *)item));
	return (buf);
}

static void
gtree_assert(int pred, const char *s, const char *func, int line)
{
	if (pred)
		return;
	printf("FAILURE: %s:%u: %s\n", func, line, s);
	gtree_print(g, stdout);
	kill(getpid(), SIGABRT);
}

static int
chknode(struct node *node)
{
	int l, r;

	CHECK(node->color == RED || node->color == BLACK);
	if (node == NIL) {
		CHECK(node->left == NIL && node->right == NIL);
		CHECK(node->color == BLACK);
		return (1);
	}
	if (node->color == RED) {
		CHECK(node->left->color == BLACK);
		CHECK(node->right->color == BLACK);
	}
	l = chknode(node->left);
	r = chknode(node->right);
	CHECK(l == r);
	return (l + (node->color == BLACK));
}

#endif	/* GTREE_TEST */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>