/*
 * libmowgli: A collection of useful routines for programming.
 * patricia.c: Dictionary-based information storage.
 *
 * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
 * Copyright (c) 2007-2010 Jilles Tjoelker <jilles -at- stack.nl>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON 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 ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "mowgli.h"

static mowgli_heap_t *leaf_heap = NULL;
static mowgli_heap_t *node_heap = NULL;

/*
 * Patricia tree.
 *
 * A radix trie that avoids one-way branching and redundant nodes.
 *
 * To find a node, the tree is traversed starting from the root. The
 * nibnum in each node indicates which nibble of the key needs to be
 * tested, and the appropriate branch is taken.
 *
 * The nibnum values are strictly increasing while going down the tree.
 *
 * -- jilles
 */

union patricia_elem;

struct mowgli_patricia_
{
	void (*canonize_cb)(char *key);
	union patricia_elem *root;

	unsigned int count;
	char *id;
};

#define POINTERS_PER_NODE 16
#define NIBBLE_VAL(key, nibnum) (((key)[(nibnum) / 2] >> ((nibnum) & 1 ? 0 : 4)) & 0xF)

struct patricia_node
{
	/* nibble to test (nibble NUM%2 of byte NUM/2) */
	int nibnum;

	/* branches of the tree */
	union patricia_elem *down[POINTERS_PER_NODE];

	union patricia_elem *parent;

	char parent_val;
};

/* mowgli_patricia_elem_ is the name used in mowgli_patricia.h.
 * patricia_leaf is the name historically used here. */
#define patricia_leaf mowgli_patricia_elem_

struct patricia_leaf
{
	/* -1 to indicate this is a leaf, not a node */
	int nibnum;

	/* data associated with the key */
	void *data;

	/* key (canonized copy) */
	char *key;
	union patricia_elem *parent;

	char parent_val;
};

union patricia_elem
{
	int nibnum;
	struct patricia_node node;

	struct patricia_leaf leaf;
};

#define IS_LEAF(elem) ((elem)->nibnum == -1)

/* Preserve compatibility with the old mowgli_patricia.h */
#define STATE_CUR(state) ((state)->pspare[0])
#define STATE_NEXT(state) ((state)->pspare[1])

/*
 * first_leaf()
 *
 * Find the smallest leaf hanging off a subtree.
 *
 * Inputs:
 *     - element (may be leaf or node) heading subtree
 *
 * Outputs:
 *     - lowest leaf in subtree
 *
 * Side Effects:
 *     - none
 */
static union patricia_elem *
first_leaf(union patricia_elem *delem)
{
	int val;

	while (!IS_LEAF(delem))
	{
		for (val = 0; val < POINTERS_PER_NODE; val++)
			if (delem->node.down[val] != NULL)
			{
				delem = delem->node.down[val];
				break;
			}
	}

	return delem;
}

/*
 * mowgli_patricia_create(void (*canonize_cb)(char *key))
 *
 * Dictionary object factory.
 *
 * Inputs:
 *     - function to use for canonizing keys (for example, use
 *       a function that makes the string upper case to create
 *       a patricia with case-insensitive matching)
 *
 * Outputs:
 *     - on success, a new patricia object.
 *
 * Side Effects:
 *     - if services runs out of memory and cannot allocate the object,
 *       the program will abort.
 */
mowgli_patricia_t *
mowgli_patricia_create(void (*canonize_cb)(char *key))
{
	return mowgli_patricia_create_named(NULL, canonize_cb);
}

/*
 * mowgli_patricia_create_named(const char *name,
 *     void (*canonize_cb)(char *key))
 *
 * Dictionary object factory.
 *
 * Inputs:
 *     - patricia name
 *     - function to use for canonizing keys (for example, use
 *       a function that makes the string upper case to create
 *       a patricia with case-insensitive matching)
 *
 * Outputs:
 *     - on success, a new patricia object.
 *
 * Side Effects:
 *     - if services runs out of memory and cannot allocate the object,
 *       the program will abort.
 */
mowgli_patricia_t *
mowgli_patricia_create_named(const char *name, void (*canonize_cb)(char *key))
{
	mowgli_patricia_t *dtree = mowgli_alloc(sizeof *dtree);

	dtree->canonize_cb = canonize_cb;

	if (name && *name)
		dtree->id = mowgli_strdup(name);

	if (!leaf_heap)
		leaf_heap = mowgli_heap_create(sizeof(struct patricia_leaf), 1024, BH_NOW);

	if (!node_heap)
		node_heap = mowgli_heap_create(sizeof(struct patricia_node), 128, BH_NOW);

	dtree->root = NULL;

	return dtree;
}

/*
 * mowgli_patricia_shutdown(void)
 *
 * Clean up after patricia to ensure all memory is released as soon as
 * possible (destroys both heaps).
 *
 * Inputs:
 *     - nothing
 *
 * Outputs:
 *     - nothing
 *
 * Side Effects:
 *     - patricia's internal heaps are destroyed and deallocated
 */
void
mowgli_patricia_shutdown(void)
{
	if (leaf_heap)
		mowgli_heap_destroy(leaf_heap);

	if (node_heap)
		mowgli_heap_destroy(node_heap);

	return;
}

/*
 * mowgli_patricia_destroy(mowgli_patricia_t *dtree,
 *     void (*destroy_cb)(const char *key, void *data, void *privdata),
 *     void *privdata);
 *
 * Recursively destroys all nodes in a patricia tree.
 *
 * Inputs:
 *     - patricia tree object
 *     - optional iteration callback
 *     - optional opaque/private data to pass to callback
 *
 * Outputs:
 *     - nothing
 *
 * Side Effects:
 *     - on success, a dtree and optionally it's children are destroyed.
 *
 * Notes:
 *     - if this is called without a callback, the objects bound to the
 *       DTree will not be destroyed.
 */
void
mowgli_patricia_destroy(mowgli_patricia_t *dtree, void (*destroy_cb)(const char *key, void *data, void *privdata), void *privdata)
{
	mowgli_patricia_iteration_state_t state;
	union patricia_elem *delem;

	void *entry;

	return_if_fail(dtree != NULL);

	MOWGLI_PATRICIA_FOREACH(entry, &state, dtree)
	{
		delem = STATE_CUR(&state);

		if (destroy_cb != NULL)
			(*destroy_cb)(delem->leaf.key, delem->leaf.data,
				      privdata);

		mowgli_patricia_delete(dtree, delem->leaf.key);
	}

	mowgli_free(dtree);
}

/*
 * mowgli_patricia_foreach(mowgli_patricia_t *dtree,
 *     int (*foreach_cb)(const char *key, void *data, void *privdata),
 *     void *privdata);
 *
 * Iterates over all entries in a DTree.
 *
 * Inputs:
 *     - patricia tree object
 *     - optional iteration callback
 *     - optional opaque/private data to pass to callback
 *
 * Outputs:
 *     - nothing
 *
 * Side Effects:
 *     - on success, a dtree is iterated
 */
void
mowgli_patricia_foreach(mowgli_patricia_t *dtree, int (*foreach_cb)(const char *key, void *data, void *privdata), void *privdata)
{
	union patricia_elem *delem, *next;

	int val;

	return_if_fail(dtree != NULL);

	delem = dtree->root;

	if (delem == NULL)
		return;

	/* Only one element in the tree */
	if (IS_LEAF(delem))
	{
		if (foreach_cb != NULL)
			(*foreach_cb)(delem->leaf.key, delem->leaf.data, privdata);

		return;
	}

	val = 0;

	do
	{
		do
			next = delem->node.down[val++];
		while (next == NULL && val < POINTERS_PER_NODE);

		if (next != NULL)
		{
			if (IS_LEAF(next))
			{
				if (foreach_cb != NULL)
					(*foreach_cb)(next->leaf.key, next->leaf.data, privdata);
			}
			else
			{
				delem = next;
				val = 0;
			}
		}

		while (val >= POINTERS_PER_NODE)
		{
			val = delem->node.parent_val;
			delem = delem->node.parent;

			if (delem == NULL)
				break;

			val++;
		}
	} while (delem != NULL);
}

/*
 * mowgli_patricia_search(mowgli_patricia_t *dtree,
 *     void *(*foreach_cb)(const char *key, void *data, void *privdata),
 *     void *privdata);
 *
 * Searches all entries in a DTree using a custom callback.
 *
 * Inputs:
 *     - patricia tree object
 *     - optional iteration callback
 *     - optional opaque/private data to pass to callback
 *
 * Outputs:
 *     - on success, the requested object
 *     - on failure, NULL.
 *
 * Side Effects:
 *     - a dtree is iterated until the requested conditions are met
 */
void *
mowgli_patricia_search(mowgli_patricia_t *dtree, void *(*foreach_cb)(const char *key, void *data, void *privdata), void *privdata)
{
	union patricia_elem *delem, *next;

	int val;
	void *ret = NULL;

	return_val_if_fail(dtree != NULL, NULL);

	delem = dtree->root;

	if (delem == NULL)
		return NULL;

	/* Only one element in the tree */
	if (IS_LEAF(delem))
	{
		if (foreach_cb != NULL)
			return (*foreach_cb)(delem->leaf.key, delem->leaf.data, privdata);

		return NULL;
	}

	val = 0;

	for (;;)
	{
		do
			next = delem->node.down[val++];
		while (next == NULL && val < POINTERS_PER_NODE);

		if (next != NULL)
		{
			if (IS_LEAF(next))
			{
				if (foreach_cb != NULL)
					ret = (*foreach_cb)(next->leaf.key, next->leaf.data, privdata);

				if (ret != NULL)
					break;
			}
			else
			{
				delem = next;
				val = 0;
			}
		}

		while (val >= POINTERS_PER_NODE)
		{
			val = delem->node.parent_val;
			delem = delem->node.parent;

			if (delem == NULL)
				break;

			val++;
		}
	}

	return ret;
}

/*
 * mowgli_patricia_foreach_start(mowgli_patricia_t *dtree,
 *     mowgli_patricia_iteration_state_t *state);
 *
 * Initializes a static DTree iterator.
 *
 * Inputs:
 *     - patricia tree object
 *     - static DTree iterator
 *
 * Outputs:
 *     - nothing
 *
 * Side Effects:
 *     - the static iterator, &state, is initialized.
 */
void
mowgli_patricia_foreach_start(mowgli_patricia_t *dtree, mowgli_patricia_iteration_state_t *state)
{
	if (dtree == NULL)
		return;

	return_if_fail(state != NULL);

	if (dtree->root != NULL)
		STATE_NEXT(state) = first_leaf(dtree->root);
	else
		STATE_NEXT(state) = NULL;

	STATE_CUR(state) = STATE_NEXT(state);

	if (STATE_NEXT(state) == NULL)
		return;

	/* make STATE_CUR point to first item and STATE_NEXT point to
	 * second item */
	mowgli_patricia_foreach_next(dtree, state);
}

/*
 * mowgli_patricia_foreach_cur(mowgli_patricia_t *dtree,
 *     mowgli_patricia_iteration_state_t *state);
 *
 * Returns the data from the current node being iterated by the
 * static iterator.
 *
 * Inputs:
 *     - patricia tree object
 *     - static DTree iterator
 *
 * Outputs:
 *     - reference to data in the current dtree node being iterated
 *
 * Side Effects:
 *     - none
 */
void *
mowgli_patricia_foreach_cur(mowgli_patricia_t *dtree, mowgli_patricia_iteration_state_t *state)
{
	if (dtree == NULL)
		return NULL;

	return_val_if_fail(state != NULL, NULL);

	return STATE_CUR(state) != NULL ?
	       ((struct patricia_leaf *) STATE_CUR(state))->data : NULL;
}

/*
 * mowgli_patricia_foreach_next(mowgli_patricia_t *dtree,
 *     mowgli_patricia_iteration_state_t *state);
 *
 * Advances a static DTree iterator.
 *
 * Inputs:
 *     - patricia tree object
 *     - static DTree iterator
 *
 * Outputs:
 *     - nothing
 *
 * Side Effects:
 *     - the static iterator, &state, is advanced to a new DTree node.
 */
void
mowgli_patricia_foreach_next(mowgli_patricia_t *dtree, mowgli_patricia_iteration_state_t *state)
{
	struct patricia_leaf *leaf;

	union patricia_elem *delem, *next;

	int val;

	if (dtree == NULL)
		return;

	return_if_fail(state != NULL);

	if (STATE_CUR(state) == NULL)
	{
		mowgli_log("mowgli_patricia_foreach_next(): called again after iteration finished on dtree<%p>", (void *) dtree);
		return;
	}

	STATE_CUR(state) = STATE_NEXT(state);

	if (STATE_NEXT(state) == NULL)
		return;

	leaf = STATE_NEXT(state);
	delem = leaf->parent;
	val = leaf->parent_val;

	while (delem != NULL)
	{
		do
			next = delem->node.down[val++];
		while (next == NULL && val < POINTERS_PER_NODE);

		if (next != NULL)
		{
			if (IS_LEAF(next))
			{
				/* We will find the original leaf first. */
				if (&next->leaf != leaf)
				{
					if (strcmp(next->leaf.key, leaf->key) < 0)
					{
						mowgli_log("mowgli_patricia_foreach_next(): iteration went backwards (libmowgli bug) on dtree<%p>", (void *) dtree);
						STATE_NEXT(state) = NULL;
						return;
					}

					STATE_NEXT(state) = next;
					return;
				}
			}
			else
			{
				delem = next;
				val = 0;
			}
		}

		while (val >= POINTERS_PER_NODE)
		{
			val = delem->node.parent_val;
			delem = delem->node.parent;

			if (delem == NULL)
				break;

			val++;
		}
	}

	STATE_NEXT(state) = NULL;
}

/*
 * mowgli_patricia_elem_find(mowgli_patricia_t *dtree, const char *key)
 *
 * Looks up a DTree node by name.
 *
 * Inputs:
 *     - patricia tree object
 *     - name of node to lookup
 *
 * Outputs:
 *     - on success, the dtree node requested
 *     - on failure, NULL
 *
 * Side Effects:
 *     - none
 */
struct patricia_leaf *
mowgli_patricia_elem_find(mowgli_patricia_t *dict, const char *key)
{
	char ckey_store[256];

	char *ckey_buf = NULL;
	const char *ckey;
	union patricia_elem *delem;

	int val, keylen;

	return_val_if_fail(dict != NULL, NULL);
	return_val_if_fail(key != NULL, NULL);

	keylen = strlen(key);

	if (dict->canonize_cb == NULL)
	{
		ckey = key;
	}
	else
	{
		if (keylen >= (int) sizeof(ckey_store))
		{
			ckey_buf = mowgli_strdup(key);
			dict->canonize_cb(ckey_buf);
			ckey = ckey_buf;
		}
		else
		{
			mowgli_strlcpy(ckey_store, key, sizeof ckey_store);
			dict->canonize_cb(ckey_store);
			ckey = ckey_store;
		}
	}

	delem = dict->root;

	while (delem != NULL && !IS_LEAF(delem))
	{
		if (delem->nibnum / 2 < keylen)
			val = NIBBLE_VAL(ckey, delem->nibnum);
		else
			val = 0;

		delem = delem->node.down[val];
	}

	/* Now, if the key is in the tree, delem contains it. */
	if ((delem != NULL) && strcmp(delem->leaf.key, ckey))
		delem = NULL;

	if (ckey_buf != NULL)
		mowgli_free(ckey_buf);

	if (delem == NULL)
		return NULL;

	return &delem->leaf;
}

/*
 * mowgli_patricia_add(mowgli_patricia_t *dtree, const char *key, void *data)
 *
 * Creates a new DTree node and binds data to it.
 *
 * Inputs:
 *     - patricia tree object
 *     - name for new DTree node
 *     - data to bind to the new DTree node
 *
 * Outputs:
 *     - on success, TRUE
 *     - on failure, FALSE
 *
 * Side Effects:
 *     - data is inserted into the DTree.
 */
struct patricia_leaf *
mowgli_patricia_elem_add(mowgli_patricia_t *dict, const char *key, void *data)
{
	char *ckey;

	union patricia_elem *delem, *prev, *newnode;

	union patricia_elem **place1;

	int val, keylen;
	int i, j;

	return_val_if_fail(dict != NULL, NULL);
	return_val_if_fail(key != NULL, NULL);
	return_val_if_fail(data != NULL, NULL);

	keylen = strlen(key);
	ckey = mowgli_strdup(key);

	if (ckey == NULL)
	{
		mowgli_log("major WTF: ckey is NULL, not adding node.");
		return NULL;
	}

	if (dict->canonize_cb != NULL)
		dict->canonize_cb(ckey);

	prev = NULL;
	val = POINTERS_PER_NODE + 2;	/* trap value */
	delem = dict->root;

	while (delem != NULL && !IS_LEAF(delem))
	{
		prev = delem;

		if (delem->nibnum / 2 < keylen)
			val = NIBBLE_VAL(ckey, delem->nibnum);
		else
			val = 0;

		delem = delem->node.down[val];
	}

	/* Now, if the key is in the tree, delem contains it. */
	if ((delem != NULL) && !strcmp(delem->leaf.key, ckey))
	{
		mowgli_log("Key is already in dict, ignoring duplicate");
		mowgli_free(ckey);
		return NULL;
	}

	if ((delem == NULL) && (prev != NULL))
		/* Get a leaf to compare with. */
		delem = first_leaf(prev);

	if (delem == NULL)
	{
		soft_assert(prev == NULL);
		soft_assert(dict->count == 0);
		place1 = &dict->root;
		*place1 = mowgli_heap_alloc(leaf_heap);
		return_val_if_fail(*place1 != NULL, NULL);
		(*place1)->nibnum = -1;
		(*place1)->leaf.data = data;
		(*place1)->leaf.key = ckey;
		(*place1)->leaf.parent = prev;
		(*place1)->leaf.parent_val = val;
		dict->count++;
		return &(*place1)->leaf;
	}

	/* Find the first nibble where they differ. */
	for (i = 0; NIBBLE_VAL(ckey, i) == NIBBLE_VAL(delem->leaf.key, i); i++)
		;

	/* Find where to insert the new node. */
	while (prev != NULL && prev->nibnum > i)
	{
		val = prev->node.parent_val;
		prev = prev->node.parent;
	}

	if ((prev == NULL) || (prev->nibnum < i))
	{
		/* Insert new node below prev */
		newnode = mowgli_heap_alloc(node_heap);
		return_val_if_fail(newnode != NULL, NULL);
		newnode->nibnum = i;
		newnode->node.parent = prev;
		newnode->node.parent_val = val;

		for (j = 0; j < POINTERS_PER_NODE; j++)
			newnode->node.down[j] = NULL;

		if (prev == NULL)
		{
			newnode->node.down[NIBBLE_VAL(delem->leaf.key, i)] = dict->root;

			if (IS_LEAF(dict->root))
			{
				dict->root->leaf.parent = newnode;
				dict->root->leaf.parent_val = NIBBLE_VAL(delem->leaf.key, i);
			}
			else
			{
				soft_assert(dict->root->nibnum > i);
				dict->root->node.parent = newnode;
				dict->root->node.parent_val = NIBBLE_VAL(delem->leaf.key, i);
			}

			dict->root = newnode;
		}
		else
		{
			newnode->node.down[NIBBLE_VAL(delem->leaf.key, i)] = prev->node.down[val];

			if (IS_LEAF(prev->node.down[val]))
			{
				prev->node.down[val]->leaf.parent = newnode;
				prev->node.down[val]->leaf.parent_val = NIBBLE_VAL(delem->leaf.key, i);
			}
			else
			{
				prev->node.down[val]->node.parent = newnode;
				prev->node.down[val]->node.parent_val = NIBBLE_VAL(delem->leaf.key, i);
			}

			prev->node.down[val] = newnode;
		}
	}
	else
	{
		/* This nibble is already checked. */
		soft_assert(prev->nibnum == i);
		newnode = prev;
	}

	val = NIBBLE_VAL(ckey, i);
	place1 = &newnode->node.down[val];
	soft_assert(*place1 == NULL);
	*place1 = mowgli_heap_alloc(leaf_heap);
	return_val_if_fail(*place1 != NULL, NULL);
	(*place1)->nibnum = -1;
	(*place1)->leaf.data = data;
	(*place1)->leaf.key = ckey;
	(*place1)->leaf.parent = newnode;
	(*place1)->leaf.parent_val = val;
	dict->count++;
	return &(*place1)->leaf;
}

mowgli_boolean_t
mowgli_patricia_add(mowgli_patricia_t *dict, const char *key, void *data)
{
	return (mowgli_patricia_elem_add(dict, key, data) != NULL) ? TRUE : FALSE;
}

/*
 * mowgli_patricia_delete(mowgli_patricia_t *dtree, const char *key)
 *
 * Deletes data from a patricia tree.
 *
 * Inputs:
 *     - patricia tree object
 *     - name of DTree node to delete
 *
 * Outputs:
 *     - on success, the remaining data that needs to be mowgli_freed
 *     - on failure, NULL
 *
 * Side Effects:
 *     - data is removed from the DTree.
 *
 * Notes:
 *     - the returned data needs to be mowgli_freed/released manually!
 */
void *
mowgli_patricia_delete(mowgli_patricia_t *dict, const char *key)
{
	void *data;
	struct patricia_leaf *leaf;

	leaf = mowgli_patricia_elem_find(dict, key);

	if (leaf == NULL)
		return NULL;

	data = leaf->data;
	mowgli_patricia_elem_delete(dict, leaf);
	return data;
}

void
mowgli_patricia_elem_delete(mowgli_patricia_t *dict, struct patricia_leaf *leaf)
{
	union patricia_elem *delem, *prev, *next;

	int val, i, used;

	return_if_fail(dict != NULL);
	return_if_fail(leaf != NULL);

	delem = (union patricia_elem *) leaf;

	val = delem->leaf.parent_val;
	prev = delem->leaf.parent;

	mowgli_free(delem->leaf.key);
	mowgli_heap_free(leaf_heap, delem);

	if (prev != NULL)
	{
		prev->node.down[val] = NULL;

		/* Leaf is gone, now consider the node it was in. */
		delem = prev;

		used = -1;

		for (i = 0; i < POINTERS_PER_NODE; i++)
			if (delem->node.down[i] != NULL)
				used = used == -1 ? i : -2;

		soft_assert(used == -2 || used >= 0);

		if (used >= 0)
		{
			/* Only one pointer in this node, remove it.
			 * Replace the pointer that pointed to it by
			 * the sole pointer in it.
			 */
			next = delem->node.down[used];
			val = delem->node.parent_val;
			prev = delem->node.parent;

			if (prev != NULL)
				prev->node.down[val] = next;
			else
				dict->root = next;

			if (IS_LEAF(next))
			{
				next->leaf.parent = prev;
				next->leaf.parent_val = val;
			}
			else
			{
				next->node.parent = prev;
				next->node.parent_val = val;
			}

			mowgli_heap_free(node_heap, delem);
		}
	}
	else
	{
		/* This was the last leaf. */
		dict->root = NULL;
	}

	dict->count--;

	if (dict->count == 0)
	{
		soft_assert(dict->root == NULL);
		dict->root = NULL;
	}
}

/*
 * mowgli_patricia_retrieve(mowgli_patricia_t *dtree, const char *key)
 *
 * Retrieves data from a patricia.
 *
 * Inputs:
 *     - patricia tree object
 *     - name of node to lookup
 *
 * Outputs:
 *     - on success, the data bound to the DTree node.
 *     - on failure, NULL
 *
 * Side Effects:
 *     - none
 */
void *
mowgli_patricia_retrieve(mowgli_patricia_t *dtree, const char *key)
{
	struct patricia_leaf *delem = mowgli_patricia_elem_find(dtree, key);

	if (delem != NULL)
		return delem->data;

	return NULL;
}

const char *
mowgli_patricia_elem_get_key(struct patricia_leaf *leaf)
{
	return_val_if_fail(leaf != NULL, NULL);

	return leaf->key;
}

void
mowgli_patricia_elem_set_data(struct patricia_leaf *leaf, void *data)
{
	return_if_fail(leaf != NULL);

	leaf->data = data;
}

void *
mowgli_patricia_elem_get_data(struct patricia_leaf *leaf)
{
	return_val_if_fail(leaf != NULL, NULL);

	return leaf->data;
}

/*
 * mowgli_patricia_size(mowgli_patricia_t *dict)
 *
 * Returns the size of a patricia.
 *
 * Inputs:
 *     - patricia tree object
 *
 * Outputs:
 *     - size of patricia
 *
 * Side Effects:
 *     - none
 */
unsigned int
mowgli_patricia_size(mowgli_patricia_t *dict)
{
	return_val_if_fail(dict != NULL, 0);

	return dict->count;
}

/* returns the sum of the depths of the subtree rooted in delem at depth depth */
/* there is no need for this to be recursive, but it is easier... */
static int
stats_recurse(union patricia_elem *delem, unsigned int depth, unsigned int *pmaxdepth)
{
	unsigned int result = 0;
	int val;
	union patricia_elem *next;

	if (depth > *pmaxdepth)
		*pmaxdepth = depth;

	if (depth == 0)
	{
		if (IS_LEAF(delem))
			soft_assert(delem->leaf.parent == NULL);

		else
			soft_assert(delem->node.parent == NULL);
	}

	if (IS_LEAF(delem))
		return depth;

	for (val = 0; val < POINTERS_PER_NODE; val++)
	{
		next = delem->node.down[val];

		if (next == NULL)
			continue;

		result += stats_recurse(next, depth + 1, pmaxdepth);

		if (IS_LEAF(next))
		{
			soft_assert(next->leaf.parent == delem);
			soft_assert(next->leaf.parent_val == val);
		}
		else
		{
			soft_assert(next->node.parent == delem);
			soft_assert(next->node.parent_val == val);
			soft_assert(next->node.nibnum > delem->node.nibnum);
		}
	}

	return result;
}

/*
 * mowgli_patricia_stats(mowgli_patricia_t *dict, void (*cb)(const char *line, void *privdata), void *privdata)
 *
 * Returns the size of a patricia.
 *
 * Inputs:
 *     - patricia tree object
 *     - callback
 *     - data for callback
 *
 * Outputs:
 *     - none
 *
 * Side Effects:
 *     - callback called with stats text
 */
void
mowgli_patricia_stats(mowgli_patricia_t *dict, void (*cb)(const char *line, void *privdata), void *privdata)
{
	char str[256];
	unsigned int sum, maxdepth;

	return_if_fail(dict != NULL);

	if (dict->id != NULL)
		snprintf(str, sizeof str, "Dictionary stats for %s (%u)",
			 dict->id, dict->count);
	else
		snprintf(str, sizeof str, "Dictionary stats for <%p> (%u)",
			 (void *) dict, dict->count);

	cb(str, privdata);
	maxdepth = 0;

	if (dict->count > 0)
	{
		sum = stats_recurse(dict->root, 0, &maxdepth);
		snprintf(str, sizeof str, "Depth sum %u Avg depth %u Max depth %u", sum, sum / dict->count, maxdepth);
	}
	else
	{
		snprintf(str, sizeof str, "Depth sum 0 Avg depth 0 Max depth 0");
	}

	cb(str, privdata);
	return;
}
