Merge branch 'gtree-add-iterators' into 'master'

GTree: add an ability to iterate over a tree and a node-based API

See merge request GNOME/glib!1509
This commit is contained in:
Philip Withnall 2020-10-06 13:43:18 +00:00
commit 7bfd6278b9
4 changed files with 695 additions and 61 deletions

View File

@ -2982,21 +2982,36 @@ g_bytes_get_type
<TITLE>Balanced Binary Trees</TITLE>
<FILE>trees-binary</FILE>
GTree
GTreeNode
g_tree_new
g_tree_ref
g_tree_unref
g_tree_new_with_data
g_tree_new_full
g_tree_node_first
g_tree_node_last
g_tree_node_previous
g_tree_node_next
g_tree_insert_node
g_tree_insert
g_tree_replace_node
g_tree_replace
g_tree_node_key
g_tree_node_value
g_tree_nnodes
g_tree_height
g_tree_lookup_node
g_tree_lookup
g_tree_lookup_extended
g_tree_foreach_node
g_tree_foreach
g_tree_traverse
GTraverseFunc
GTraverseNodeFunc
g_tree_search_node
g_tree_search
g_tree_lower_bound
g_tree_upper_bound
g_tree_remove
g_tree_steal
g_tree_destroy

View File

@ -66,12 +66,8 @@
* To destroy a #GTree, use g_tree_destroy().
**/
#undef G_TREE_DEBUG
#define MAX_GTREE_HEIGHT 40
typedef struct _GTreeNode GTreeNode;
/**
* GTree:
*
@ -104,10 +100,10 @@ struct _GTreeNode
static GTreeNode* g_tree_node_new (gpointer key,
gpointer value);
static void g_tree_insert_internal (GTree *tree,
gpointer key,
gpointer value,
gboolean replace);
static GTreeNode *g_tree_insert_internal (GTree *tree,
gpointer key,
gpointer value,
gboolean replace);
static gboolean g_tree_remove_internal (GTree *tree,
gconstpointer key,
gboolean steal);
@ -123,9 +119,9 @@ static gint g_tree_node_in_order (GTreeNode *node,
static gint g_tree_node_post_order (GTreeNode *node,
GTraverseFunc traverse_func,
gpointer data);
static gpointer g_tree_node_search (GTreeNode *node,
GCompareFunc search_func,
gconstpointer data);
static GTreeNode *g_tree_node_search (GTreeNode *node,
GCompareFunc search_func,
gconstpointer data);
static GTreeNode* g_tree_node_rotate_left (GTreeNode *node);
static GTreeNode* g_tree_node_rotate_right (GTreeNode *node);
#ifdef G_TREE_DEBUG
@ -230,11 +226,24 @@ g_tree_new_full (GCompareDataFunc key_compare_func,
return tree;
}
static inline GTreeNode *
g_tree_first_node (GTree *tree)
/**
* g_tree_node_first:
* @tree: a #GTree
*
* Returns the first in-order node of the tree, or %NULL
* for an empty tree.
*
* Returns: (nullable) (transfer none): the first node in the tree
*
* Since: 2.68
*/
GTreeNode *
g_tree_node_first (GTree *tree)
{
GTreeNode *tmp;
g_return_val_if_fail (tree != NULL, NULL);
if (!tree->root)
return NULL;
@ -244,13 +253,55 @@ g_tree_first_node (GTree *tree)
tmp = tmp->left;
return tmp;
}
}
static inline GTreeNode *
/**
* g_tree_node_last:
* @tree: a #GTree
*
* Returns the last in-order node of the tree, or %NULL
* for an empty tree.
*
* Returns: (nullable) (transfer none): the last node in the tree
*
* Since: 2.68
*/
GTreeNode *
g_tree_node_last (GTree *tree)
{
GTreeNode *tmp;
g_return_val_if_fail (tree != NULL, NULL);
if (!tree->root)
return NULL;
tmp = tree->root;
while (tmp->right_child)
tmp = tmp->right;
return tmp;
}
/**
* g_tree_node_previous
* @node: a #GTree node
*
* Returns the previous in-order node of the tree, or %NULL
* if the passed node was already the first one.
*
* Returns: (nullable) (transfer none): the previous node in the tree
*
* Since: 2.68
*/
GTreeNode *
g_tree_node_previous (GTreeNode *node)
{
GTreeNode *tmp;
g_return_val_if_fail (node != NULL, NULL);
tmp = node->left;
if (node->left_child)
@ -260,11 +311,24 @@ g_tree_node_previous (GTreeNode *node)
return tmp;
}
static inline GTreeNode *
/**
* g_tree_node_next
* @node: a #GTree node
*
* Returns the next in-order node of the tree, or %NULL
* if the passed node was already the last one.
*
* Returns: (nullable) (transfer none): the next node in the tree
*
* Since: 2.68
*/
GTreeNode *
g_tree_node_next (GTreeNode *node)
{
GTreeNode *tmp;
g_return_val_if_fail (node != NULL, NULL);
tmp = node->right;
if (node->right_child)
@ -282,7 +346,7 @@ g_tree_remove_all (GTree *tree)
g_return_if_fail (tree != NULL);
node = g_tree_first_node (tree);
node = g_tree_node_first (tree);
while (node)
{
@ -294,11 +358,22 @@ g_tree_remove_all (GTree *tree)
tree->value_destroy_func (node->value);
g_slice_free (GTreeNode, node);
#ifdef G_TREE_DEBUG
g_assert (tree->nnodes > 0);
tree->nnodes--;
#endif
node = next;
}
#ifdef G_TREE_DEBUG
g_assert (tree->nnodes == 0);
#endif
tree->root = NULL;
#ifndef G_TREE_DEBUG
tree->nnodes = 0;
#endif
}
/**
@ -369,7 +444,7 @@ g_tree_destroy (GTree *tree)
}
/**
* g_tree_insert:
* g_tree_insert_node:
* @tree: a #GTree
* @key: the key to insert
* @value: the value corresponding to the key
@ -387,28 +462,55 @@ g_tree_destroy (GTree *tree)
* The cost of maintaining a balanced tree while inserting new key/value
* result in a O(n log(n)) operation where most of the other operations
* are O(log(n)).
*
* Returns: (transfer none): the inserted (or set) node.
*
* Since: 2.68
*/
GTreeNode *
g_tree_insert_node (GTree *tree,
gpointer key,
gpointer value)
{
GTreeNode *node;
g_return_val_if_fail (tree != NULL, NULL);
node = g_tree_insert_internal (tree, key, value, FALSE);
#ifdef G_TREE_DEBUG
g_tree_node_check (tree->root);
#endif
return node;
}
/**
* g_tree_insert:
* @tree: a #GTree
* @key: the key to insert
* @value: the value corresponding to the key
*
* Inserts a key/value pair into a #GTree.
*
* Inserts a new key and value into a #GTree as g_tree_insert_node() does,
* only this function does not return the inserted or set node.
*/
void
g_tree_insert (GTree *tree,
gpointer key,
gpointer value)
{
g_return_if_fail (tree != NULL);
g_tree_insert_internal (tree, key, value, FALSE);
#ifdef G_TREE_DEBUG
g_tree_node_check (tree->root);
#endif
g_tree_insert_node (tree, key, value);
}
/**
* g_tree_replace:
* g_tree_replace_node:
* @tree: a #GTree
* @key: the key to insert
* @value: the value corresponding to the key
*
* Inserts a new key and value into a #GTree similar to g_tree_insert().
*
* Inserts a new key and value into a #GTree similar to g_tree_insert_node().
* The difference is that if the key already exists in the #GTree, it gets
* replaced by the new key. If you supplied a @value_destroy_func when
* creating the #GTree, the old value is freed using that function. If you
@ -417,39 +519,64 @@ g_tree_insert (GTree *tree,
*
* The tree is automatically 'balanced' as new key/value pairs are added,
* so that the distance from the root to every leaf is as small as possible.
*
* Returns: (transfer none): the inserted (or set) node.
*
* Since: 2.68
*/
GTreeNode *
g_tree_replace_node (GTree *tree,
gpointer key,
gpointer value)
{
GTreeNode *node;
g_return_val_if_fail (tree != NULL, NULL);
node = g_tree_insert_internal (tree, key, value, TRUE);
#ifdef G_TREE_DEBUG
g_tree_node_check (tree->root);
#endif
return node;
}
/**
* g_tree_replace:
* @tree: a #GTree
* @key: the key to insert
* @value: the value corresponding to the key
*
* Inserts a new key and value into a #GTree as g_tree_replace_node() does,
* only this function does not return the inserted or set node.
*/
void
g_tree_replace (GTree *tree,
gpointer key,
gpointer value)
{
g_return_if_fail (tree != NULL);
g_tree_insert_internal (tree, key, value, TRUE);
#ifdef G_TREE_DEBUG
g_tree_node_check (tree->root);
#endif
g_tree_replace_node (tree, key, value);
}
/* internal insert routine */
static void
static GTreeNode *
g_tree_insert_internal (GTree *tree,
gpointer key,
gpointer value,
gboolean replace)
{
GTreeNode *node;
GTreeNode *node, *retnode;
GTreeNode *path[MAX_GTREE_HEIGHT];
int idx;
g_return_if_fail (tree != NULL);
g_return_val_if_fail (tree != NULL, NULL);
if (!tree->root)
{
tree->root = g_tree_node_new (key, value);
tree->nnodes++;
return;
return tree->root;
}
idx = 0;
@ -481,7 +608,7 @@ g_tree_insert_internal (GTree *tree,
tree->key_destroy_func (key);
}
return;
return node;
}
else if (cmp < 0)
{
@ -502,6 +629,7 @@ g_tree_insert_internal (GTree *tree,
tree->nnodes++;
retnode = child;
break;
}
}
@ -524,6 +652,7 @@ g_tree_insert_internal (GTree *tree,
tree->nnodes++;
retnode = child;
break;
}
}
@ -560,6 +689,8 @@ g_tree_insert_internal (GTree *tree,
node = bparent;
}
return retnode;
}
/**
@ -835,6 +966,65 @@ g_tree_remove_internal (GTree *tree,
return TRUE;
}
/**
* g_tree_node_key:
* @node: a #GTree node
*
* Gets the key stored at a particular tree node.
*
* Returns: (nullable) (transfer none): the key at the node.
*
* Since: 2.68
*/
gpointer
g_tree_node_key (GTreeNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
return node->key;
}
/**
* g_tree_node_value:
* @node: a #GTree node
*
* Gets the value stored at a particular tree node.
*
* Returns: (nullable) (transfer none): the value at the node.
*
* Since: 2.68
*/
gpointer
g_tree_node_value (GTreeNode *node)
{
g_return_val_if_fail (node != NULL, NULL);
return node->value;
}
/**
* g_tree_lookup_node:
* @tree: a #GTree
* @key: the key to look up
*
* Gets the tree node corresponding to the given key. Since a #GTree is
* automatically balanced as key/value pairs are added, key lookup
* is O(log n) (where n is the number of key/value pairs in the tree).
*
* Returns: (nullable) (transfer none): the tree node corresponding to
* the key, or %NULL if the key was not found
*
* Since: 2.68
*/
GTreeNode *
g_tree_lookup_node (GTree *tree,
gconstpointer key)
{
g_return_val_if_fail (tree != NULL, NULL);
return g_tree_find_node (tree, key);
}
/**
* g_tree_lookup:
* @tree: a #GTree
@ -853,10 +1043,8 @@ g_tree_lookup (GTree *tree,
{
GTreeNode *node;
g_return_val_if_fail (tree != NULL, NULL);
node = g_tree_lookup_node (tree, key);
node = g_tree_find_node (tree, key);
return node ? node->value : NULL;
}
@ -926,8 +1114,8 @@ g_tree_foreach (GTree *tree,
if (!tree->root)
return;
node = g_tree_first_node (tree);
node = g_tree_node_first (tree);
while (node)
{
if ((*func) (node->key, node->value, user_data))
@ -937,6 +1125,47 @@ g_tree_foreach (GTree *tree,
}
}
/**
* g_tree_foreach_node:
* @tree: a #GTree
* @func: the function to call for each node visited.
* If this function returns %TRUE, the traversal is stopped.
* @user_data: user data to pass to the function
*
* Calls the given function for each of the nodes in the #GTree.
* The function is passed the pointer to the particular node, and the given
* @data parameter. The tree traversal happens in-order.
*
* The tree may not be modified while iterating over it (you can't
* add/remove items). To remove all items matching a predicate, you need
* to add each item to a list in your #GTraverseFunc as you walk over
* the tree, then walk the list and remove each item.
*
* Since: 2.68
*/
void
g_tree_foreach_node (GTree *tree,
GTraverseNodeFunc func,
gpointer user_data)
{
GTreeNode *node;
g_return_if_fail (tree != NULL);
if (!tree->root)
return;
node = g_tree_node_first (tree);
while (node)
{
if ((*func) (node, user_data))
break;
node = g_tree_node_next (node);
}
}
/**
* g_tree_traverse:
* @tree: a #GTree
@ -997,6 +1226,40 @@ g_tree_traverse (GTree *tree,
}
}
/**
* g_tree_search_node:
* @tree: a #GTree
* @search_func: a function used to search the #GTree
* @user_data: the data passed as the second argument to @search_func
*
* Searches a #GTree using @search_func.
*
* The @search_func is called with a pointer to the key of a key/value
* pair in the tree, and the passed in @user_data. If @search_func returns
* 0 for a key/value pair, then the corresponding node is returned as
* the result of g_tree_search(). If @search_func returns -1, searching
* will proceed among the key/value pairs that have a smaller key; if
* @search_func returns 1, searching will proceed among the key/value
* pairs that have a larger key.
*
* Returns: (nullable) (transfer none): the node corresponding to the
* found key, or %NULL if the key was not found
*
* Since: 2.68
*/
GTreeNode *
g_tree_search_node (GTree *tree,
GCompareFunc search_func,
gconstpointer user_data)
{
g_return_val_if_fail (tree != NULL, NULL);
if (!tree->root)
return NULL;
return g_tree_node_search (tree->root, search_func, user_data);
}
/**
* g_tree_search:
* @tree: a #GTree
@ -1021,12 +1284,119 @@ g_tree_search (GTree *tree,
GCompareFunc search_func,
gconstpointer user_data)
{
GTreeNode *node;
node = g_tree_search_node (tree, search_func, user_data);
return node ? node->value : NULL;
}
/**
* g_tree_lower_bound:
* @tree: a #GTree
* @key: the key to calculate the lower bound for
*
* Gets the lower bound node corresponding to the given key,
* or %NULL if the tree is empty or all the nodes in the tree
* have keys that are strictly lower than the searched key.
*
* The lower bound is the first node that has its key greater
* than or equal to the searched key.
*
* Returns: (nullable) (transfer none): the tree node corresponding to
* the lower bound, or %NULL if the tree is empty or has only
* keys strictly lower than the searched key.
*
* Since: 2.68
*/
GTreeNode *
g_tree_lower_bound (GTree *tree,
gconstpointer key)
{
GTreeNode *node, *result;
gint cmp;
g_return_val_if_fail (tree != NULL, NULL);
if (tree->root)
return g_tree_node_search (tree->root, search_func, user_data);
else
node = tree->root;
if (!node)
return NULL;
result = NULL;
while (1)
{
cmp = tree->key_compare (key, node->key, tree->key_compare_data);
if (cmp <= 0)
{
result = node;
if (!node->left_child)
return result;
node = node->left;
}
else
{
if (!node->right_child)
return result;
node = node->right;
}
}
}
/**
* g_tree_upper_bound:
* @tree: a #GTree
* @key: the key to calculate the upper bound for
*
* Gets the upper bound node corresponding to the given key,
* or %NULL if the tree is empty or all the nodes in the tree
* have keys that are lower than or equal to the searched key.
*
* The upper bound is the first node that has its key strictly greater
* than the searched key.
*
* Returns: (nullable) (transfer none): the tree node corresponding to the
* upper bound, or %NULL if the tree is empty or has only keys
* lower than or equal to the searched key.
*
* Since: 2.68
*/
GTreeNode *
g_tree_upper_bound (GTree *tree,
gconstpointer key)
{
GTreeNode *node, *result;
gint cmp;
g_return_val_if_fail (tree != NULL, NULL);
node = tree->root;
if (!node)
return NULL;
result = NULL;
while (1)
{
cmp = tree->key_compare (key, node->key, tree->key_compare_data);
if (cmp < 0)
{
result = node;
if (!node->left_child)
return result;
node = node->left;
}
else
{
if (!node->right_child)
return result;
node = node->right;
}
}
}
/**
@ -1203,7 +1573,7 @@ g_tree_node_post_order (GTreeNode *node,
return FALSE;
}
static gpointer
static GTreeNode *
g_tree_node_search (GTreeNode *node,
GCompareFunc search_func,
gconstpointer data)
@ -1217,7 +1587,7 @@ g_tree_node_search (GTreeNode *node,
{
dir = (* search_func) (node->key, data);
if (dir == 0)
return node->value;
return node;
else if (dir < 0)
{
if (!node->left_child)
@ -1388,17 +1758,22 @@ g_tree_node_dump (GTreeNode *node,
g_print ("%*s%c\n", indent, "", *(char *)node->key);
if (node->left_child)
g_tree_node_dump (node->left, indent + 2);
{
g_print ("%*sLEFT\n", indent, "");
g_tree_node_dump (node->left, indent + 2);
}
else if (node->left)
g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->key);
if (node->right_child)
g_tree_node_dump (node->right, indent + 2);
{
g_print ("%*sRIGHT\n", indent, "");
g_tree_node_dump (node->right, indent + 2);
}
else if (node->right)
g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->key);
}
void
g_tree_dump (GTree *tree)
{

View File

@ -33,12 +33,39 @@
G_BEGIN_DECLS
#undef G_TREE_DEBUG
typedef struct _GTree GTree;
/**
* GTreeNode:
*
* An opaque type which identifies a specific node in a #GTree.
*
* Since: 2.68
*/
typedef struct _GTreeNode GTreeNode;
typedef gboolean (*GTraverseFunc) (gpointer key,
gpointer value,
gpointer data);
/**
* GTraverseNodeFunc:
* @node: a #GTreeNode
* @data: user data passed to g_tree_foreach_node()
*
* Specifies the type of function passed to g_tree_foreach_node(). It is
* passed each node, together with the @user_data parameter passed to
* g_tree_foreach_node(). If the function returns %TRUE, the traversal is
* stopped.
*
* Returns: %TRUE to stop the traversal
* Since: 2.68
*/
typedef gboolean (*GTraverseNodeFunc) (GTreeNode *node,
gpointer data);
/* Balanced binary trees
*/
GLIB_AVAILABLE_IN_ALL
@ -51,16 +78,32 @@ GTree* g_tree_new_full (GCompareDataFunc key_compare_func,
gpointer key_compare_data,
GDestroyNotify key_destroy_func,
GDestroyNotify value_destroy_func);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_node_first (GTree *tree);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_node_last (GTree *tree);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_node_previous (GTreeNode *node);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_node_next (GTreeNode *node);
GLIB_AVAILABLE_IN_ALL
GTree* g_tree_ref (GTree *tree);
GLIB_AVAILABLE_IN_ALL
void g_tree_unref (GTree *tree);
GLIB_AVAILABLE_IN_ALL
void g_tree_destroy (GTree *tree);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_insert_node (GTree *tree,
gpointer key,
gpointer value);
GLIB_AVAILABLE_IN_ALL
void g_tree_insert (GTree *tree,
gpointer key,
gpointer value);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_replace_node (GTree *tree,
gpointer key,
gpointer value);
GLIB_AVAILABLE_IN_ALL
void g_tree_replace (GTree *tree,
gpointer key,
@ -71,6 +114,13 @@ gboolean g_tree_remove (GTree *tree,
GLIB_AVAILABLE_IN_ALL
gboolean g_tree_steal (GTree *tree,
gconstpointer key);
GLIB_AVAILABLE_IN_2_68
gpointer g_tree_node_key (GTreeNode *node);
GLIB_AVAILABLE_IN_2_68
gpointer g_tree_node_value (GTreeNode *node);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_lookup_node (GTree *tree,
gconstpointer key);
GLIB_AVAILABLE_IN_ALL
gpointer g_tree_lookup (GTree *tree,
gconstpointer key);
@ -83,6 +133,10 @@ GLIB_AVAILABLE_IN_ALL
void g_tree_foreach (GTree *tree,
GTraverseFunc func,
gpointer user_data);
GLIB_AVAILABLE_IN_2_68
void g_tree_foreach_node (GTree *tree,
GTraverseNodeFunc func,
gpointer user_data);
GLIB_DEPRECATED
void g_tree_traverse (GTree *tree,
@ -90,15 +144,32 @@ void g_tree_traverse (GTree *tree,
GTraverseType traverse_type,
gpointer user_data);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_search_node (GTree *tree,
GCompareFunc search_func,
gconstpointer user_data);
GLIB_AVAILABLE_IN_ALL
gpointer g_tree_search (GTree *tree,
GCompareFunc search_func,
gconstpointer user_data);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_lower_bound (GTree *tree,
gconstpointer key);
GLIB_AVAILABLE_IN_2_68
GTreeNode *g_tree_upper_bound (GTree *tree,
gconstpointer key);
GLIB_AVAILABLE_IN_ALL
gint g_tree_height (GTree *tree);
GLIB_AVAILABLE_IN_ALL
gint g_tree_nnodes (GTree *tree);
#ifdef G_TREE_DEBUG
/*< private >*/
#ifndef __GTK_DOC_IGNORE__
void g_tree_dump (GTree *tree);
#endif /* !__GTK_DOC_IGNORE__ */
#endif /* G_TREE_DEBUG */
G_END_DECLS
#endif /* __G_TREE_H__ */

View File

@ -397,9 +397,139 @@ my_traverse (gpointer key,
return FALSE;
}
static void
binary_tree_bound (GTree *tree,
char c,
char expected,
int lower)
{
GTreeNode *node;
if (lower)
node = g_tree_lower_bound (tree, &c);
else
node = g_tree_upper_bound (tree, &c);
if (g_test_verbose ())
g_printerr ("%c %s: ", c, lower ? "lower" : "upper");
if (!node)
{
if (!g_tree_nnodes (tree))
{
if (g_test_verbose ())
g_printerr ("empty tree");
}
else
{
GTreeNode *last = g_tree_node_last (tree);
g_assert (last);
if (g_test_verbose ())
g_printerr ("past end last %c",
*(char *) g_tree_node_key (last));
}
g_assert (expected == '\x00');
}
else
{
GTreeNode *begin = g_tree_node_first (tree);
GTreeNode *last = g_tree_node_last (tree);
GTreeNode *prev = g_tree_node_previous (node);
GTreeNode *next = g_tree_node_next (node);
g_assert (expected != '\x00');
g_assert (expected == *(char *) g_tree_node_key (node));
if (g_test_verbose ())
g_printerr ("%c", *(char *) g_tree_node_key (node));
if (node != begin)
{
g_assert (prev);
if (g_test_verbose ())
g_printerr (" prev %c", *(char *) g_tree_node_key (prev));
}
else
{
g_assert (!prev);
if (g_test_verbose ())
g_printerr (" no prev, it's the first one");
}
if (node != last)
{
g_assert (next);
if (g_test_verbose ())
g_printerr (" next %c", *(char *) g_tree_node_key (next));
}
else
{
g_assert (!next);
if (g_test_verbose ())
g_printerr (" no next, it's the last one");
}
}
if (g_test_verbose ())
g_printerr ("\n");
}
static void
binary_tree_bounds (GTree *tree,
char c,
int mode)
{
char expectedl, expectedu;
char first = mode == 0 ? '0' : mode == 1 ? 'A' : 'z';
g_assert (mode >= 0 && mode <= 3);
if (c < first)
expectedl = first;
else if (c > 'z')
expectedl = '\x00';
else
expectedl = c;
if (c < first)
expectedu = first;
else if (c >= 'z')
expectedu = '\x00';
else
expectedu = c == '9' ? 'A' : c == 'Z' ? 'a' : c + 1;
if (mode == 3)
{
expectedl = '\x00';
expectedu = '\x00';
}
binary_tree_bound (tree, c, expectedl, 1);
binary_tree_bound (tree, c, expectedu, 0);
}
static void
binary_tree_bounds_test (GTree *tree,
int mode)
{
binary_tree_bounds (tree, 'a', mode);
binary_tree_bounds (tree, 'A', mode);
binary_tree_bounds (tree, 'z', mode);
binary_tree_bounds (tree, 'Z', mode);
binary_tree_bounds (tree, 'Y', mode);
binary_tree_bounds (tree, '0', mode);
binary_tree_bounds (tree, '9', mode);
binary_tree_bounds (tree, '0' - 1, mode);
binary_tree_bounds (tree, 'z' + 1, mode);
binary_tree_bounds (tree, '0' - 2, mode);
binary_tree_bounds (tree, 'z' + 2, mode);
}
static void
binary_tree_test (void)
{
GQueue queue = G_QUEUE_INIT;
GTree *tree;
char chars[62];
guint i, j;
@ -409,42 +539,85 @@ binary_tree_test (void)
for (j = 0; j < 10; j++, i++)
{
chars[i] = '0' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
g_queue_push_tail (&queue, &chars[i]);
}
for (j = 0; j < 26; j++, i++)
{
chars[i] = 'A' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
g_queue_push_tail (&queue, &chars[i]);
}
for (j = 0; j < 26; j++, i++)
{
chars[i] = 'a' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
g_queue_push_tail (&queue, &chars[i]);
}
g_assert_cmpint (g_tree_nnodes (tree), ==, 10 + 26 + 26);
g_assert_cmpint (g_tree_height (tree), ==, 6);
if (g_test_verbose ())
g_printerr ("tree insert: ");
while (!g_queue_is_empty (&queue))
{
gint32 which = g_random_int_range (0, g_queue_get_length (&queue));
gpointer elem = g_queue_pop_nth (&queue, which);
GTreeNode *node;
if (g_test_verbose())
if (g_test_verbose ())
g_printerr ("%c ", *(char *) elem);
node = g_tree_insert_node (tree, elem, elem);
g_assert (g_tree_node_key (node) == elem);
g_assert (g_tree_node_value (node) == elem);
}
if (g_test_verbose ())
g_printerr ("\n");
g_assert_cmpint (g_tree_nnodes (tree), ==, 10 + 26 + 26);
g_assert_cmpint (g_tree_height (tree), >=, 6);
g_assert_cmpint (g_tree_height (tree), <=, 8);
if (g_test_verbose ())
{
g_printerr ("tree: ");
g_tree_foreach (tree, my_traverse, NULL);
g_printerr ("\n");
}
binary_tree_bounds_test (tree, 0);
for (i = 0; i < 10; i++)
g_tree_remove (tree, &chars[i]);
g_assert_cmpint (g_tree_nnodes (tree), ==, 26 + 26);
g_assert_cmpint (g_tree_height (tree), ==, 6);
g_assert_cmpint (g_tree_height (tree), >=, 6);
g_assert_cmpint (g_tree_height (tree), <=, 8);
if (g_test_verbose())
if (g_test_verbose ())
{
g_printerr ("tree: ");
g_tree_foreach (tree, my_traverse, NULL);
g_printerr ("\n");
}
binary_tree_bounds_test (tree, 1);
for (i = 10; i < 10 + 26 + 26 - 1; i++)
g_tree_remove (tree, &chars[i]);
if (g_test_verbose ())
{
g_printerr ("tree: ");
g_tree_foreach (tree, my_traverse, NULL);
g_printerr ("\n");
}
binary_tree_bounds_test (tree, 2);
g_tree_remove (tree, &chars[10 + 26 + 26 - 1]);
if (g_test_verbose ())
g_printerr ("empty tree\n");
binary_tree_bounds_test (tree, 3);
g_tree_unref (tree);
}