/*
 * Copyright (c) 2003, 2004 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-bsd-tree-0.c,v 1.4 2004/12/26 04:08:53 ca Exp $")

#include "sm/assert.h"
#include "sm/heap.h"
#include "sm/test.h"
#include "sm/bsd-tree.h"

#include <stdio.h>

uint state[] =
{
	0x934a0b83,
	0x589ce730,
	0x938cde01,
	0x20992cea,
	0x49120598,
	0xc492ce22,
	0x092c98ef,
	0x68013da8,
	0x34927491,
	0x78934289,
	0xc8734ea8,
	0x2390437c,
	0x8c09a24f,
	0x494710ee,
	0x94892188,
	0x94810cbf
};

typedef struct node node_T, *node_P;
typedef struct t_tree t_tree_T, *t_tree_P;

RB_HEAD(t_tree, node);

struct node
{
	RB_ENTRY(node)	entry;
	uint		key;
	long		value;
	node_P		next;
};

#if 0
static int
compare(long oa, long ob)
{
	if (oa < ob)
		return -1;
	else if (oa == ob)
		return 0;
	else
		return 1;
}
#endif /* 0 */

static int
compare_nodes(node_P oa, node_P ob)
{
	long a;
	long b;

	a = oa->key;
	b = ob->key;
	if (a < b)
		return -1;
	else if (a == b)
		return 0;
	else
		return 1;
}

RB_GENERATE(t_tree, node, entry, compare_nodes)

node_P	 root;
int	 limit;
int	*hist;
long	*keys;

#define TREE_SEARCH(elm, r) (elm).key = keys[r], RB_FIND(t_tree, tree, &(elm))

#define TREE_RM(r)					\
	do 						\
	{ 						\
		node_T elm;				\
		elm.key = keys[r];			\
		RB_REMOVE(t_tree, tree, &elm);		\
	} while (0)

static int
validate_node(node_T *current, int *count, int check_order, int check_heights)
{
	int left_height;
	int right_height;
	int height;

	right_height = 0;
	left_height = 0;
	if (count != NULL)
		(*count)++;
	if (current->entry.rbe_left != NULL)
	{
		SM_TEST(current->entry.rbe_left->entry.rbe_parent == current);

		if (check_order)
		{
			SM_TEST(compare_nodes(current,
					current->entry.rbe_left) == 1);
		}
		left_height = validate_node(current->entry.rbe_left, count,
						check_order, check_heights);
	}

	if (current->entry.rbe_right != NULL)
	{
		SM_TEST(current->entry.rbe_right->entry.rbe_parent == current);
		if (check_order)
			SM_TEST(compare_nodes(current,
					current->entry.rbe_right) == -1);
		right_height = validate_node(current->entry.rbe_right, count,
						check_order, check_heights);
	}
	if (check_heights)
	{
		SM_TEST(abs(right_height - left_height) <= 2);
		if (abs(right_height - left_height) > 2)
		{
			fprintf(stderr, "right_height=%d, left_height=%d\n",
				right_height, left_height);
		}
	}

/*
	if (check_heights)
	{
		if (left_height >entry. right_height)
			SM_TEST(current->entry.an_balance == b_left);
		else if (right_height > left_height)
			SM_TEST(current->entry.an_balance == b_right);
		else
			SM_TEST(current->entry.an_balance == b_even);
	}
*/
	height = SM_MAX(right_height, left_height) + 1;
	return height;
}

static int
validate_tree(t_tree_T *otree, int check_order, int check_heights)
{
	t_tree_T *tree;
	node_T *current;
	int count;
	int height;

	tree = otree;
	current = tree->rbh_root;
	count = 0;
	if (current != NULL)
		height = validate_node(current, &count,
					check_order, check_heights);
	return count;
}

static int
validate_tree2(t_tree_T *tree)
{
	node_T *current;
	int count;

	current = RB_MIN(t_tree, tree);
	count = 0;
	while (current != NULL)
	{
		current = RB_NEXT(t_tree, tree, current);
		++count;
	}
	return count;
}

static int
validate_tree3(t_tree_T *tree)
{
	int i;
	long value;
	node_T *node, *next, elm;

	for (i = 0; i < limit; i++)
	{
		value = random() % limit;
#if 0
		node = TREE_SEARCH(elm, value);
#else
		(elm).key = keys[value];
		node = RB_FIND(t_tree, tree, &(elm));
#endif
		if (node == NULL)
			continue;
		while ((next = RB_NEXT(t_tree, tree, node)) != NULL)
		{
			SM_TEST(compare_nodes(node, next));
			node = next;
		}
	}
	return 0;
}

static void
validate_chain(t_tree_T *otree, int size)
{
	t_tree_T *tree;
	node_T *current;
	node_T *next;
	int count;

	tree = otree;
	current = RB_MIN(t_tree, tree);
	if (current == NULL)
	{
		SM_TEST(size == 0);
		return;
	}
	while (current->entry.rbe_left != NULL)
		current = current->entry.rbe_left;

	count = 1;
	do
	{
		next = RB_NEXT(t_tree, otree, current);
		if (next != NULL)
		{
			count++;
/*
			SM_TEST(((node_T*) next)->value >
				  ((node_T*) current)->value);
*/
			SM_TEST(compare_nodes(next, current) == 1);

		}
		current = next;
	} while (next != NULL);
	SM_TEST(count == size);
	return;
}

int
main(int argc, char* *argv)
{
	t_tree_T tree;
	node_T *tree_node;
	node_T *node, elm, *result;
	int  i, j;
	int  size;
	int  validated_size;
	int  insertions;
	int  deletions;
	int c;

	root = NULL;
	limit = 200;
	hist = NULL;
	keys = NULL;
	while ((c = getopt(argc, argv, "s:v")) != -1)
	{
		switch (c)
		{
		  case 's':
			limit = atoi(optarg);
			if (limit <= 0)
				return 1;
			break;
		}
	}
	sm_test_begin(argc, argv, "test bsd-tree-0");
	argc -= optind;
	argv += optind;

	initstate((ulong) 0x482094872L, (char *) state, sizeof state);
	RB_INIT(&tree);
	hist = malloc(sizeof(*hist) * (limit + 1));
	SM_TEST(hist != NULL);
	if (hist == NULL)
		goto error;
	keys = malloc(sizeof(*keys) * (limit + 1));
	SM_TEST(keys != NULL);
	if (keys == NULL)
		goto error;
	size = 0;
	insertions = 0;
	deletions = 0;

	for (i = 0; i <= limit; i++)
	{
		hist[i] = 0;
  again:
		keys[i] = random();
		for (j = 0; j < i; j++)
		{
			if (keys[i] == keys[j])
				goto again;
		}
	}

	for (i = 0; i < limit; i++)
	{
		for (;;)
		{
			node = malloc(sizeof(node_T));
			if (node == NULL)
				goto error;
			node->value = random() % limit;
			node->key = keys[node->value];
#if 0
			tree_node = TREE_SEARCH(elm, node->value);
#else
			elm.key = keys[node->value];
			tree_node = RB_FIND(t_tree, &tree, &elm);
#endif
			if (tree_node == NULL)
			{
				result = RB_INSERT(t_tree, &tree, node);
				SM_TEST(result == NULL);
				if (result == NULL)
				{
					size++;
					node->next = root;
					root = node;
					insertions++;
					break;
				}
				else
				{
					fprintf(stderr,
						"result=%p, node=%p, result->value=%ld, node->value=%ld\n",
						result, node, result->value,
						node->value);
					free(node);
				}
			}
			else
				free(node);
		}

		validated_size = validate_tree(&tree, true, true);
		SM_TEST(validated_size == size);
		if (validated_size != size)
		{
			SmTestVerbose = 1;
			SM_TEST(validated_size == validate_tree2(&tree));
		}
		validate_chain(&tree, size);
		if (SmTestVerbose && (i % 1000 == 0))
			printf("Interation %d, size %d (%d i, %d d)\n",
				i, size, insertions, deletions);

		SM_TEST(size <= limit);
		hist[size]++;
	}
	validate_tree3(&tree);
	if (SmTestVerbose)
	{
		printf("%-6.6s %-8.8s\n", "  Size", "  Count");
		for (i = 0; i <= limit; i++)
			printf("%6d %8d\n", i, hist [i]);
	}
  error:
#if 0
	sm_tree_destroy(&tree);
#endif /* 0 */
	if (keys != NULL)
		free(keys);
	if (hist != NULL)
		free(hist);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1