added a GNode test.
authorTim Janik <timj@gtk.org>
Fri, 31 Jul 1998 20:21:10 +0000 (20:21 +0000)
committerTim Janik <timj@src.gnome.org>
Fri, 31 Jul 1998 20:21:10 +0000 (20:21 +0000)
Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>

        * testglib.c (g_node_test): added a GNode test.

Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>

        * Makefile.am: compile gnode.c.

        * glib.h:
        * gnode.c: added implementation of n-way trees.

        * gtree.c (g_tree_traverse): added a warning to the switch() statement
        which says that G_LEVEL_ORDER is not implemented.

18 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
Makefile.am
glib.h
glib/Makefile.am
glib/glib.h
glib/gnode.c [new file with mode: 0644]
glib/gtree.c
gnode.c [new file with mode: 0644]
gtree.c
testglib.c
tests/testglib.c

index e334e34..9ba7458 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>
+
+       * testglib.c (g_node_test): added a GNode test.
+
+Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>
+
+       * Makefile.am: compile gnode.c.
+
+       * glib.h: 
+       * gnode.c: added implementation of n-way trees.
+
+       * gtree.c (g_tree_traverse): added a warning to the switch() statement
+       which says that G_LEVEL_ORDER is not implemented.
+
 Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org>
 
        * Released GLib 1.1.0
index e334e34..9ba7458 100644 (file)
@@ -1,3 +1,17 @@
+Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>
+
+       * testglib.c (g_node_test): added a GNode test.
+
+Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>
+
+       * Makefile.am: compile gnode.c.
+
+       * glib.h: 
+       * gnode.c: added implementation of n-way trees.
+
+       * gtree.c (g_tree_traverse): added a warning to the switch() statement
+       which says that G_LEVEL_ORDER is not implemented.
+
 Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org>
 
        * Released GLib 1.1.0
index e334e34..9ba7458 100644 (file)
@@ -1,3 +1,17 @@
+Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>
+
+       * testglib.c (g_node_test): added a GNode test.
+
+Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>
+
+       * Makefile.am: compile gnode.c.
+
+       * glib.h: 
+       * gnode.c: added implementation of n-way trees.
+
+       * gtree.c (g_tree_traverse): added a warning to the switch() statement
+       which says that G_LEVEL_ORDER is not implemented.
+
 Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org>
 
        * Released GLib 1.1.0
index e334e34..9ba7458 100644 (file)
@@ -1,3 +1,17 @@
+Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>
+
+       * testglib.c (g_node_test): added a GNode test.
+
+Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>
+
+       * Makefile.am: compile gnode.c.
+
+       * glib.h: 
+       * gnode.c: added implementation of n-way trees.
+
+       * gtree.c (g_tree_traverse): added a warning to the switch() statement
+       which says that G_LEVEL_ORDER is not implemented.
+
 Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org>
 
        * Released GLib 1.1.0
index e334e34..9ba7458 100644 (file)
@@ -1,3 +1,17 @@
+Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>
+
+       * testglib.c (g_node_test): added a GNode test.
+
+Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>
+
+       * Makefile.am: compile gnode.c.
+
+       * glib.h: 
+       * gnode.c: added implementation of n-way trees.
+
+       * gtree.c (g_tree_traverse): added a warning to the switch() statement
+       which says that G_LEVEL_ORDER is not implemented.
+
 Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org>
 
        * Released GLib 1.1.0
index e334e34..9ba7458 100644 (file)
@@ -1,3 +1,17 @@
+Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>
+
+       * testglib.c (g_node_test): added a GNode test.
+
+Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>
+
+       * Makefile.am: compile gnode.c.
+
+       * glib.h: 
+       * gnode.c: added implementation of n-way trees.
+
+       * gtree.c (g_tree_traverse): added a warning to the switch() statement
+       which says that G_LEVEL_ORDER is not implemented.
+
 Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org>
 
        * Released GLib 1.1.0
index e334e34..9ba7458 100644 (file)
@@ -1,3 +1,17 @@
+Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>
+
+       * testglib.c (g_node_test): added a GNode test.
+
+Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>
+
+       * Makefile.am: compile gnode.c.
+
+       * glib.h: 
+       * gnode.c: added implementation of n-way trees.
+
+       * gtree.c (g_tree_traverse): added a warning to the switch() statement
+       which says that G_LEVEL_ORDER is not implemented.
+
 Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org>
 
        * Released GLib 1.1.0
index e334e34..9ba7458 100644 (file)
@@ -1,3 +1,17 @@
+Fri Jul 31 22:17:05 1998  Tim Janik  <timj@gtk.org>
+
+       * testglib.c (g_node_test): added a GNode test.
+
+Fri Jul 31 09:08:16 1998  Tim Janik  <timj@gtk.org>
+
+       * Makefile.am: compile gnode.c.
+
+       * glib.h: 
+       * gnode.c: added implementation of n-way trees.
+
+       * gtree.c (g_tree_traverse): added a warning to the switch() statement
+       which says that G_LEVEL_ORDER is not implemented.
+
 Mon Jul 27 00:17:30 CDT 1998 Shawn T. Amundson <amundson@gtk.org>
 
        * Released GLib 1.1.0
index 7bcc118..50faf48 100644 (file)
@@ -20,6 +20,7 @@ libglib_1_1_la_SOURCES = \
                glist.c         \
                gmem.c          \
                gmessages.c     \
+               gnode.c         \
                gprimes.c       \
                gslist.c        \
                gtimer.c        \
diff --git a/glib.h b/glib.h
index 69ad8ad..b48ed11 100644 (file)
--- a/glib.h
+++ b/glib.h
@@ -475,6 +475,7 @@ typedef union  _GValue              GValue;
 typedef struct _GCompletion    GCompletion;
 typedef struct _GRelation      GRelation;
 typedef struct _GTuples                GTuples;
+typedef struct _GNode          GNode;
 
 
 typedef void           (*GFunc)                (gpointer  data,
@@ -488,6 +489,10 @@ typedef void               (*GCacheDestroyFunc)    (gpointer  value);
 typedef gint           (*GTraverseFunc)        (gpointer  key,
                                                 gpointer  value,
                                                 gpointer  data);
+typedef gboolean       (*GNodeTraverseFunc)    (GNode    *node,
+                                                gpointer  data);
+typedef void           (*GNodeForeachFunc)     (GNode    *node,
+                                                gpointer  data);
 typedef gint           (*GSearchFunc)          (gpointer  key,
                                                 gpointer  data);
 typedef void           (*GErrorFunc)           (gchar    *str);
@@ -562,9 +567,18 @@ typedef enum
 {
   G_IN_ORDER,
   G_PRE_ORDER,
-  G_POST_ORDER
+  G_POST_ORDER,
+  G_LEVEL_ORDER
 } GTraverseType;
 
+typedef enum
+{
+  G_TRAVERSE_LEAFS     = 1 << 0,
+  G_TRAVERSE_NON_LEAFS = 1 << 1,
+  G_TRAVERSE_ALL       = G_TRAVERSE_LEAFS | G_TRAVERSE_NON_LEAFS,
+  G_TRAVERSE_MASK      = 0x03
+} GTraverseFlags;
+
 /* Doubly linked lists
  */
 GList* g_list_alloc            (void);
@@ -707,7 +721,7 @@ void         g_cache_value_foreach (GCache            *cache,
                                gpointer           user_data);
 
 
-/* Trees
+/* Balanced binary trees
  */
 GTree*  g_tree_new      (GCompareFunc   key_compare_func);
 void    g_tree_destroy  (GTree         *tree);
@@ -729,6 +743,99 @@ gint        g_tree_height   (GTree         *tree);
 gint    g_tree_nnodes   (GTree         *tree);
 
 
+
+/* N-way tree implementation
+ */
+struct _GNode
+{
+  GNode *prev;
+  GNode *next;
+  GNode *parent;
+  GNode *children;
+  
+  gpointer data;
+};
+
+#define         G_NODE_IS_ROOT(node)   (((GNode*) (node))->parent == NULL && \
+                                ((GNode*) (node))->prev == NULL && \
+                                ((GNode*) (node))->next == NULL)
+#define         G_NODE_IS_LEAF(node)   (((GNode*) (node))->children == NULL)
+
+GNode*  g_node_new             (gpointer          data);
+void    g_node_destroy         (GNode            *root);
+void    g_node_unlink          (GNode            *node);
+void    g_node_insert          (GNode            *parent,
+                                gint              position,
+                                GNode            *node);
+void    g_node_insert_before   (GNode            *parent,
+                                GNode            *sibling,
+                                GNode            *node);
+void    g_node_prepend         (GNode            *parent,
+                                GNode            *node);
+guint   g_node_n_nodes         (GNode            *root,
+                                GTraverseFlags    flags);
+GNode*  g_node_get_root        (GNode            *node);
+gboolean g_node_is_ancestor    (GNode            *node,
+                                GNode            *descendant);
+guint   g_node_depth           (GNode            *node);
+GNode*  g_node_find            (GNode            *root,
+                                GTraverseType     order,
+                                GTraverseFlags    flags,
+                                gpointer          data);
+
+/* traversal function, assumes that `node' is root
+ * (only traverses `node' and its subtree).
+ * this function is just a high level interface to
+ * low level traversal functions, optimized for speed.
+ */
+void    g_node_traverse        (GNode            *root,
+                                GTraverseType     order,
+                                GTraverseFlags    flags,
+                                gint              max_depth,
+                                GNodeTraverseFunc func,
+                                gpointer          data);
+
+/* return the maximum tree height starting with `node', this is an expensive
+ * operation, since we need to visit all nodes. this could be shortened by
+ * adding `guint height' to struct _GNode, but then again, this is not very
+ * often needed, and would make g_node_insert() more time consuming.
+ */
+guint   g_node_max_height       (GNode *root);
+
+void    g_node_children_foreach (GNode           *node,
+                                 GTraverseFlags   flags,
+                                 GNodeForeachFunc func,
+                                 gpointer         data);
+void    g_node_reverse_children (GNode           *node);
+guint   g_node_n_children       (GNode           *node);
+GNode*  g_node_nth_child        (GNode           *node,
+                                 guint            n);
+GNode*  g_node_last_child       (GNode           *node);
+GNode*  g_node_find_child       (GNode           *node,
+                                 GTraverseFlags   flags,
+                                 gpointer         data);
+gint    g_node_child_position   (GNode           *node,
+                                 GNode           *child);
+gint    g_node_child_index      (GNode           *node,
+                                 gpointer         data);
+
+GNode*  g_node_first_sibling    (GNode           *node);
+GNode*  g_node_last_sibling     (GNode           *node);
+
+#define         g_node_prev_sibling(node)      ((node) ? \
+                                        ((GNode*) (node))->prev : NULL)
+#define         g_node_next_sibling(node)      ((node) ? \
+                                        ((GNode*) (node))->next : NULL)
+#define         g_node_first_child(node)       ((node) ? \
+                                        ((GNode*) (node))->children : NULL)
+#define         g_node_append(parent, node)    G_STMT_START { \
+                                         g_node_insert_before ((parent), \
+                                                               NULL, \
+                                                               (node)); \
+                                       } G_STMT_END
+
+
+
 /* Memory
  */
 
index 7bcc118..50faf48 100644 (file)
@@ -20,6 +20,7 @@ libglib_1_1_la_SOURCES = \
                glist.c         \
                gmem.c          \
                gmessages.c     \
+               gnode.c         \
                gprimes.c       \
                gslist.c        \
                gtimer.c        \
index 69ad8ad..b48ed11 100644 (file)
@@ -475,6 +475,7 @@ typedef union  _GValue              GValue;
 typedef struct _GCompletion    GCompletion;
 typedef struct _GRelation      GRelation;
 typedef struct _GTuples                GTuples;
+typedef struct _GNode          GNode;
 
 
 typedef void           (*GFunc)                (gpointer  data,
@@ -488,6 +489,10 @@ typedef void               (*GCacheDestroyFunc)    (gpointer  value);
 typedef gint           (*GTraverseFunc)        (gpointer  key,
                                                 gpointer  value,
                                                 gpointer  data);
+typedef gboolean       (*GNodeTraverseFunc)    (GNode    *node,
+                                                gpointer  data);
+typedef void           (*GNodeForeachFunc)     (GNode    *node,
+                                                gpointer  data);
 typedef gint           (*GSearchFunc)          (gpointer  key,
                                                 gpointer  data);
 typedef void           (*GErrorFunc)           (gchar    *str);
@@ -562,9 +567,18 @@ typedef enum
 {
   G_IN_ORDER,
   G_PRE_ORDER,
-  G_POST_ORDER
+  G_POST_ORDER,
+  G_LEVEL_ORDER
 } GTraverseType;
 
+typedef enum
+{
+  G_TRAVERSE_LEAFS     = 1 << 0,
+  G_TRAVERSE_NON_LEAFS = 1 << 1,
+  G_TRAVERSE_ALL       = G_TRAVERSE_LEAFS | G_TRAVERSE_NON_LEAFS,
+  G_TRAVERSE_MASK      = 0x03
+} GTraverseFlags;
+
 /* Doubly linked lists
  */
 GList* g_list_alloc            (void);
@@ -707,7 +721,7 @@ void         g_cache_value_foreach (GCache            *cache,
                                gpointer           user_data);
 
 
-/* Trees
+/* Balanced binary trees
  */
 GTree*  g_tree_new      (GCompareFunc   key_compare_func);
 void    g_tree_destroy  (GTree         *tree);
@@ -729,6 +743,99 @@ gint        g_tree_height   (GTree         *tree);
 gint    g_tree_nnodes   (GTree         *tree);
 
 
+
+/* N-way tree implementation
+ */
+struct _GNode
+{
+  GNode *prev;
+  GNode *next;
+  GNode *parent;
+  GNode *children;
+  
+  gpointer data;
+};
+
+#define         G_NODE_IS_ROOT(node)   (((GNode*) (node))->parent == NULL && \
+                                ((GNode*) (node))->prev == NULL && \
+                                ((GNode*) (node))->next == NULL)
+#define         G_NODE_IS_LEAF(node)   (((GNode*) (node))->children == NULL)
+
+GNode*  g_node_new             (gpointer          data);
+void    g_node_destroy         (GNode            *root);
+void    g_node_unlink          (GNode            *node);
+void    g_node_insert          (GNode            *parent,
+                                gint              position,
+                                GNode            *node);
+void    g_node_insert_before   (GNode            *parent,
+                                GNode            *sibling,
+                                GNode            *node);
+void    g_node_prepend         (GNode            *parent,
+                                GNode            *node);
+guint   g_node_n_nodes         (GNode            *root,
+                                GTraverseFlags    flags);
+GNode*  g_node_get_root        (GNode            *node);
+gboolean g_node_is_ancestor    (GNode            *node,
+                                GNode            *descendant);
+guint   g_node_depth           (GNode            *node);
+GNode*  g_node_find            (GNode            *root,
+                                GTraverseType     order,
+                                GTraverseFlags    flags,
+                                gpointer          data);
+
+/* traversal function, assumes that `node' is root
+ * (only traverses `node' and its subtree).
+ * this function is just a high level interface to
+ * low level traversal functions, optimized for speed.
+ */
+void    g_node_traverse        (GNode            *root,
+                                GTraverseType     order,
+                                GTraverseFlags    flags,
+                                gint              max_depth,
+                                GNodeTraverseFunc func,
+                                gpointer          data);
+
+/* return the maximum tree height starting with `node', this is an expensive
+ * operation, since we need to visit all nodes. this could be shortened by
+ * adding `guint height' to struct _GNode, but then again, this is not very
+ * often needed, and would make g_node_insert() more time consuming.
+ */
+guint   g_node_max_height       (GNode *root);
+
+void    g_node_children_foreach (GNode           *node,
+                                 GTraverseFlags   flags,
+                                 GNodeForeachFunc func,
+                                 gpointer         data);
+void    g_node_reverse_children (GNode           *node);
+guint   g_node_n_children       (GNode           *node);
+GNode*  g_node_nth_child        (GNode           *node,
+                                 guint            n);
+GNode*  g_node_last_child       (GNode           *node);
+GNode*  g_node_find_child       (GNode           *node,
+                                 GTraverseFlags   flags,
+                                 gpointer         data);
+gint    g_node_child_position   (GNode           *node,
+                                 GNode           *child);
+gint    g_node_child_index      (GNode           *node,
+                                 gpointer         data);
+
+GNode*  g_node_first_sibling    (GNode           *node);
+GNode*  g_node_last_sibling     (GNode           *node);
+
+#define         g_node_prev_sibling(node)      ((node) ? \
+                                        ((GNode*) (node))->prev : NULL)
+#define         g_node_next_sibling(node)      ((node) ? \
+                                        ((GNode*) (node))->next : NULL)
+#define         g_node_first_child(node)       ((node) ? \
+                                        ((GNode*) (node))->children : NULL)
+#define         g_node_append(parent, node)    G_STMT_START { \
+                                         g_node_insert_before ((parent), \
+                                                               NULL, \
+                                                               (node)); \
+                                       } G_STMT_END
+
+
+
 /* Memory
  */
 
diff --git a/glib/gnode.c b/glib/gnode.c
new file mode 100644 (file)
index 0000000..2279de2
--- /dev/null
@@ -0,0 +1,887 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * GNode: N-way tree implementation.
+ * Copyright (C) 1998 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "glib.h"
+
+
+#define        KEEP_NODES      (1024)
+
+
+/* --- variables --- */
+static GMemChunk       *g_tree_node_chunk = NULL;
+static GNode           *free_nodes = NULL;
+static guint           n_free_nodes = 0;
+
+
+/* --- functions --- */
+GNode*
+g_node_new (gpointer data)
+{
+  register GNode *node;
+  
+  if (!g_tree_node_chunk)
+    g_tree_node_chunk = g_mem_chunk_create (GNode, 1024, G_ALLOC_AND_FREE);
+  
+  if (n_free_nodes)
+    {
+      node = free_nodes;
+      free_nodes = free_nodes->next;
+      n_free_nodes--;
+    }
+  else
+    node = g_chunk_new (GNode, g_tree_node_chunk);
+  
+  node->prev = NULL;
+  node->next = NULL;
+  node->parent = NULL;
+  node->children = NULL;
+  node->data = data;
+  
+  return node;
+}
+
+static void
+g_node_free (GNode *parent)
+{
+  GNode *node;
+
+  node = parent->children;
+  
+  while (node)
+    {
+      register GNode *free_node;
+
+      free_node = node;
+      node = free_node->next;
+      g_node_free (free_node);
+    }
+
+  if (n_free_nodes < KEEP_NODES)
+    {
+      parent->next = free_nodes;
+      free_nodes = parent;
+      n_free_nodes++;
+    }
+  else
+    g_chunk_free (parent, g_tree_node_chunk);
+}
+
+void
+g_node_destroy (GNode *root)
+{
+  g_return_if_fail (root != NULL);
+  
+  if (!G_NODE_IS_ROOT (root))
+    g_node_unlink (root);
+  
+  g_node_free (root);
+}
+
+void
+g_node_unlink (GNode *node)
+{
+  g_return_if_fail (node != NULL);
+  
+  if (node->prev)
+    node->prev->next = node->next;
+  else if (node->parent)
+    node->parent->children = node->next;
+  node->parent = NULL;
+  if (node->next)
+    {
+      node->next->prev = node->prev;
+      node->next = NULL;
+    }
+  node->prev = NULL;
+}
+
+void
+g_node_insert (GNode *parent,
+              gint   position,
+              GNode *node)
+{
+  g_return_if_fail (parent != NULL);
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (G_NODE_IS_ROOT (node));
+  
+  if (position > 0)
+    g_node_insert_before (parent,
+                         g_node_nth_child (parent, position),
+                         node);
+  else if (position == 0)
+    g_node_prepend (parent, node);
+  else if (position < 0)
+    g_node_append (parent, node);
+}
+
+void
+g_node_insert_before (GNode *parent,
+                     GNode *sibling,
+                     GNode *node)
+{
+  g_return_if_fail (parent != NULL);
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (G_NODE_IS_ROOT (node));
+  if (sibling)
+    g_return_if_fail (sibling->parent == parent);
+  
+  node->parent = parent;
+  
+  if (sibling)
+    {
+      if (sibling->prev)
+       {
+         node->prev = sibling->prev;
+         node->prev->next = node;
+         node->next = sibling;
+         sibling->prev = node;
+       }
+      else
+       {
+         node->parent->children = node;
+         node->next = sibling;
+         sibling->prev = node;
+       }
+    }
+  else
+    {
+      if (parent->children)
+       {
+         sibling = parent->children;
+         while (sibling->next)
+           sibling = sibling->next;
+         node->prev = sibling;
+         sibling->next = node;
+       }
+      else
+       node->parent->children = node;
+    }
+}
+
+void
+g_node_prepend (GNode *parent,
+               GNode *node)
+{
+  g_return_if_fail (parent != NULL);
+  
+  g_node_insert_before (parent, parent->children, node);
+}
+
+GNode*
+g_node_get_root (GNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  while (node->parent)
+    node = node->parent;
+  
+  return node;
+}
+
+gboolean
+g_node_is_ancestor (GNode *node,
+                   GNode *descendant)
+{
+  g_return_val_if_fail (node != NULL, FALSE);
+  g_return_val_if_fail (descendant != NULL, FALSE);
+  
+  while (descendant)
+    {
+      if (descendant->parent == node)
+       return TRUE;
+      
+      descendant = descendant->parent;
+    }
+  
+  return FALSE;
+}
+
+/* returns 1 for root, 2 for first level children,
+ * 3 for children's children...
+ */
+guint
+g_node_depth (GNode *node)
+{
+  register guint depth = 0;
+  
+  while (node)
+    {
+      depth++;
+      node = node->parent;
+    }
+  
+  return depth;
+}
+
+void
+g_node_reverse_children (GNode *node)
+{
+  GNode *child;
+  GNode *last;
+  
+  g_return_if_fail (node != NULL);
+  
+  child = node->children;
+  last = NULL;
+  while (child)
+    {
+      last = child;
+      child = last->next;
+      last->next = last->prev;
+      last->prev = child;
+    }
+  node->children = last;
+}
+
+guint
+g_node_max_height (GNode *root)
+{
+  register GNode *child;
+  register guint max_height = 0;
+  
+  if (!root)
+    return 0;
+  
+  child = root->children;
+  while (child)
+    {
+      register guint tmp_height;
+      
+      tmp_height = g_node_max_height (child);
+      if (tmp_height > max_height)
+       max_height = tmp_height;
+      child = child->next;
+    }
+  
+  return max_height + 1;
+}
+
+static gboolean
+g_node_traverse_pre_order (GNode           *node,
+                          GTraverseFlags    flags,
+                          GNodeTraverseFunc func,
+                          gpointer          data)
+{
+  if (node->children)
+    {
+      GNode *child;
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+      child = node->children;
+      while (child)
+       {
+         register GNode *current;
+         
+         current = child;
+         child = current->next;
+         if (g_node_traverse_pre_order (current, flags, func, data))
+           return TRUE;
+       }
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_depth_traverse_pre_order (GNode           *node,
+                                GTraverseFlags    flags,
+                                guint             depth,
+                                GNodeTraverseFunc func,
+                                gpointer          data)
+{
+  if (node->children)
+    {
+      GNode *child;
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+      depth--;
+      if (!depth)
+       return FALSE;
+      
+      child = node->children;
+      while (child)
+       {
+         register GNode *current;
+         
+         current = child;
+         child = current->next;
+         if (g_node_depth_traverse_pre_order (current, flags, depth, func, data))
+           return TRUE;
+       }
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_traverse_post_order (GNode           *node,
+                           GTraverseFlags    flags,
+                           GNodeTraverseFunc func,
+                           gpointer          data)
+{
+  if (node->children)
+    {
+      GNode *child;
+      
+      child = node->children;
+      while (child)
+       {
+         register GNode *current;
+         
+         current = child;
+         child = current->next;
+         if (g_node_traverse_post_order (current, flags, func, data))
+           return TRUE;
+       }
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_depth_traverse_post_order (GNode                   *node,
+                                 GTraverseFlags    flags,
+                                 guint             depth,
+                                 GNodeTraverseFunc func,
+                                 gpointer          data)
+{
+  if (node->children)
+    {
+      depth--;
+      if (depth)
+       {
+         GNode *child;
+         
+         child = node->children;
+         while (child)
+           {
+             register GNode *current;
+             
+             current = child;
+             child = current->next;
+             if (g_node_depth_traverse_post_order (current, flags, depth, func, data))
+               return TRUE;
+           }
+       }
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_traverse_in_order (GNode                   *node,
+                         GTraverseFlags    flags,
+                         GNodeTraverseFunc func,
+                         gpointer          data)
+{
+  if (node->children)
+    {
+      GNode *child;
+      register GNode *current;
+      
+      child = node->children;
+      current = child;
+      child = current->next;
+      
+      if (g_node_traverse_in_order (current, flags, func, data))
+       return TRUE;
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+      while (child)
+       {
+         current = child;
+         child = current->next;
+         if (g_node_traverse_in_order (current, flags, func, data))
+           return TRUE;
+       }
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_depth_traverse_in_order (GNode           *node,
+                               GTraverseFlags    flags,
+                               guint             depth,
+                               GNodeTraverseFunc func,
+                               gpointer          data)
+{
+  if (node->children)
+    {
+      depth--;
+      if (depth)
+       {
+         GNode *child;
+         register GNode *current;
+         
+         child = node->children;
+         current = child;
+         child = current->next;
+         
+         if (g_node_depth_traverse_in_order (current, flags, depth, func, data))
+           return TRUE;
+         
+         if ((flags & G_TRAVERSE_NON_LEAFS) &&
+             func (node, data))
+           return TRUE;
+         
+         while (child)
+           {
+             current = child;
+             child = current->next;
+             if (g_node_depth_traverse_in_order (current, flags, depth, func, data))
+               return TRUE;
+           }
+       }
+      else if ((flags & G_TRAVERSE_NON_LEAFS) &&
+              func (node, data))
+       return TRUE;
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_traverse_children (GNode                    *node,
+                         GTraverseFlags     flags,
+                         GNodeTraverseFunc  func,
+                         gpointer           data)
+{
+  GNode *child;
+  
+  child = node->children;
+  
+  while (child)
+    {
+      register GNode *current;
+      
+      current = child;
+      child = current->next;
+      
+      if (current->children)
+       {
+         if ((flags & G_TRAVERSE_NON_LEAFS) &&
+             func (current, data))
+           return TRUE;
+       }
+      else if ((flags & G_TRAVERSE_LEAFS) &&
+              func (current, data))
+       return TRUE;
+    }
+  
+  child = node->children;
+  
+  while (child)
+    {
+      register GNode *current;
+      
+      current = child;
+      child = current->next;
+      
+      if (current->children &&
+         g_node_traverse_children (current, flags, func, data))
+       return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_depth_traverse_children (GNode           *node,
+                               GTraverseFlags    flags,
+                               guint             depth,
+                               GNodeTraverseFunc func,
+                               gpointer          data)
+{
+  GNode *child;
+  
+  child = node->children;
+  
+  while (child)
+    {
+      register GNode *current;
+      
+      current = child;
+      child = current->next;
+      
+      if (current->children)
+       {
+         if ((flags & G_TRAVERSE_NON_LEAFS) &&
+             func (current, data))
+           return TRUE;
+       }
+      else if ((flags & G_TRAVERSE_LEAFS) &&
+              func (current, data))
+       return TRUE;
+    }
+  
+  depth--;
+  if (!depth)
+    return FALSE;
+  
+  child = node->children;
+  
+  while (child)
+    {
+      register GNode *current;
+      
+      current = child;
+      child = current->next;
+      
+      if (current->children &&
+         g_node_depth_traverse_children (current, flags, depth, func, data))
+       return TRUE;
+    }
+  
+  return FALSE;
+}
+
+void
+g_node_traverse (GNode           *root,
+                GTraverseType     order,
+                GTraverseFlags    flags,
+                gint              depth,
+                GNodeTraverseFunc func,
+                gpointer          data)
+{
+  g_return_if_fail (root != NULL);
+  g_return_if_fail (func != NULL);
+  g_return_if_fail (order <= G_LEVEL_ORDER);
+  g_return_if_fail (flags <= G_TRAVERSE_MASK);
+  g_return_if_fail (depth == -1 || depth > 0);
+  
+  switch (order)
+    {
+    case G_PRE_ORDER:
+      if (depth < 0)
+       g_node_traverse_pre_order (root, flags, func, data);
+      else
+       g_node_depth_traverse_pre_order (root, flags, depth, func, data);
+      break;
+    case G_POST_ORDER:
+      if (depth < 0)
+       g_node_traverse_post_order (root, flags, func, data);
+      else
+       g_node_depth_traverse_post_order (root, flags, depth, func, data);
+      break;
+    case G_IN_ORDER:
+      if (depth < 0)
+       g_node_traverse_in_order (root, flags, func, data);
+      else
+       g_node_depth_traverse_in_order (root, flags, depth, func, data);
+      break;
+    case G_LEVEL_ORDER:
+      if (root->children)
+       {
+         if (!((flags & G_TRAVERSE_NON_LEAFS) &&
+               func (root, data)))
+           {
+             if (depth < 0)
+               g_node_traverse_children (root, flags, func, data);
+             else
+               {
+                 depth--;
+                 if (depth)
+                   g_node_depth_traverse_children (root, flags, depth, func, data);
+               }
+           }
+       }
+      else if (flags & G_TRAVERSE_LEAFS)
+       func (root, data);
+      break;
+    }
+}
+
+static gboolean
+g_node_find_func (GNode          *node,
+                 gpointer data)
+{
+  register gpointer *d = data;
+  
+  if (*d != node->data)
+    return FALSE;
+  
+  *(++d) = node;
+  
+  return TRUE;
+}
+
+GNode*
+g_node_find (GNode            *root,
+            GTraverseType      order,
+            GTraverseFlags     flags,
+            gpointer           data)
+{
+  gpointer d[2];
+  
+  g_return_val_if_fail (root != NULL, NULL);
+  g_return_val_if_fail (order <= G_LEVEL_ORDER, NULL);
+  g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL);
+  
+  d[0] = data;
+  d[1] = NULL;
+  
+  g_node_traverse (root, order, flags, -1, g_node_find_func, d);
+  
+  return d[1];
+}
+
+static void
+g_node_count_func (GNode        *node,
+                  GTraverseFlags flags,
+                  guint         *n)
+{
+  if (node->children)
+    {
+      GNode *child;
+      
+      if (flags & G_TRAVERSE_NON_LEAFS)
+       (*n)++;
+      
+      child = node->children;
+      while (child)
+       {
+         g_node_count_func (child, flags, n);
+         child = child->next;
+       }
+    }
+  else if (flags & G_TRAVERSE_LEAFS)
+    (*n)++;
+}
+
+guint
+g_node_n_nodes (GNode        *root,
+               GTraverseFlags flags)
+{
+  guint n = 0;
+  
+  g_return_val_if_fail (root != NULL, 0);
+  g_return_val_if_fail (flags <= G_TRAVERSE_MASK, 0);
+  
+  g_node_count_func (root, flags, &n);
+  
+  return n;
+}
+
+GNode*
+g_node_last_child (GNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  node = node->children;
+  if (node)
+    while (node->next)
+      node = node->next;
+  
+  return node;
+}
+
+GNode*
+g_node_nth_child (GNode *node,
+                 guint  n)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  node = node->children;
+  if (node)
+    while ((n-- > 0) && node)
+      node = node->next;
+  
+  return node;
+}
+
+guint
+g_node_n_children (GNode *node)
+{
+  guint n = 0;
+  
+  g_return_val_if_fail (node != NULL, 0);
+  
+  node = node->children;
+  while (node)
+    {
+      n++;
+      node = node->next;
+    }
+  
+  return n;
+}
+
+GNode*
+g_node_find_child (GNode        *node,
+                  GTraverseFlags flags,
+                  gpointer       data)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL);
+  
+  node = node->children;
+  while (node)
+    {
+      if (node->data == data)
+       {
+         if (G_NODE_IS_LEAF (node))
+           {
+             if (flags & G_TRAVERSE_LEAFS)
+               return node;
+           }
+         else
+           {
+             if (flags & G_TRAVERSE_NON_LEAFS)
+               return node;
+           }
+       }
+      node = node->next;
+    }
+  
+  return NULL;
+}
+
+gint
+g_node_child_position (GNode *node,
+                      GNode *child)
+{
+  register guint n = 0;
+  
+  g_return_val_if_fail (node != NULL, -1);
+  g_return_val_if_fail (child != NULL, -1);
+  g_return_val_if_fail (child->parent == node, -1);
+  
+  node = node->children;
+  while (node)
+    {
+      if (node == child)
+       return n;
+      n++;
+      node = node->next;
+    }
+  
+  return -1;
+}
+
+gint
+g_node_child_index (GNode   *node,
+                   gpointer data)
+{
+  register guint n = 0;
+  
+  g_return_val_if_fail (node != NULL, -1);
+  
+  node = node->children;
+  while (node)
+    {
+      if (node->data == data)
+       return n;
+      n++;
+      node = node->next;
+    }
+  
+  return -1;
+}
+
+GNode*
+g_node_first_sibling (GNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  while (node->prev)
+    node = node->prev;
+  
+  return node;
+}
+
+GNode*
+g_node_last_sibling (GNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  while (node->next)
+    node = node->next;
+  
+  return node;
+}
+
+void
+g_node_children_foreach (GNode          *node,
+                        GTraverseFlags   flags,
+                        GNodeForeachFunc func,
+                        gpointer         data)
+{
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (flags <= G_TRAVERSE_MASK);
+  g_return_if_fail (func != NULL);
+  
+  node = node->children;
+  while (node)
+    {
+      register GNode *current;
+      
+      current = node;
+      node = current->next;
+      if (G_NODE_IS_LEAF (node))
+       {
+         if (flags & G_TRAVERSE_LEAFS)
+           func (current, data);
+       }
+      else
+       {
+         if (flags & G_TRAVERSE_NON_LEAFS)
+           func (current, data);
+       }
+    }
+}
index 981ff39..2a3dd8f 100644 (file)
@@ -177,6 +177,10 @@ g_tree_traverse (GTree         *tree,
     case G_POST_ORDER:
       g_tree_node_post_order (rtree->root, traverse_func, data);
       break;
+    
+    case G_LEVEL_ORDER:
+      g_warning ("g_tree_traverse(): traverse type G_LEVEL_ORDER isn't implemented.");
+      break;
     }
 }
 
diff --git a/gnode.c b/gnode.c
new file mode 100644 (file)
index 0000000..2279de2
--- /dev/null
+++ b/gnode.c
@@ -0,0 +1,887 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * GNode: N-way tree implementation.
+ * Copyright (C) 1998 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "glib.h"
+
+
+#define        KEEP_NODES      (1024)
+
+
+/* --- variables --- */
+static GMemChunk       *g_tree_node_chunk = NULL;
+static GNode           *free_nodes = NULL;
+static guint           n_free_nodes = 0;
+
+
+/* --- functions --- */
+GNode*
+g_node_new (gpointer data)
+{
+  register GNode *node;
+  
+  if (!g_tree_node_chunk)
+    g_tree_node_chunk = g_mem_chunk_create (GNode, 1024, G_ALLOC_AND_FREE);
+  
+  if (n_free_nodes)
+    {
+      node = free_nodes;
+      free_nodes = free_nodes->next;
+      n_free_nodes--;
+    }
+  else
+    node = g_chunk_new (GNode, g_tree_node_chunk);
+  
+  node->prev = NULL;
+  node->next = NULL;
+  node->parent = NULL;
+  node->children = NULL;
+  node->data = data;
+  
+  return node;
+}
+
+static void
+g_node_free (GNode *parent)
+{
+  GNode *node;
+
+  node = parent->children;
+  
+  while (node)
+    {
+      register GNode *free_node;
+
+      free_node = node;
+      node = free_node->next;
+      g_node_free (free_node);
+    }
+
+  if (n_free_nodes < KEEP_NODES)
+    {
+      parent->next = free_nodes;
+      free_nodes = parent;
+      n_free_nodes++;
+    }
+  else
+    g_chunk_free (parent, g_tree_node_chunk);
+}
+
+void
+g_node_destroy (GNode *root)
+{
+  g_return_if_fail (root != NULL);
+  
+  if (!G_NODE_IS_ROOT (root))
+    g_node_unlink (root);
+  
+  g_node_free (root);
+}
+
+void
+g_node_unlink (GNode *node)
+{
+  g_return_if_fail (node != NULL);
+  
+  if (node->prev)
+    node->prev->next = node->next;
+  else if (node->parent)
+    node->parent->children = node->next;
+  node->parent = NULL;
+  if (node->next)
+    {
+      node->next->prev = node->prev;
+      node->next = NULL;
+    }
+  node->prev = NULL;
+}
+
+void
+g_node_insert (GNode *parent,
+              gint   position,
+              GNode *node)
+{
+  g_return_if_fail (parent != NULL);
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (G_NODE_IS_ROOT (node));
+  
+  if (position > 0)
+    g_node_insert_before (parent,
+                         g_node_nth_child (parent, position),
+                         node);
+  else if (position == 0)
+    g_node_prepend (parent, node);
+  else if (position < 0)
+    g_node_append (parent, node);
+}
+
+void
+g_node_insert_before (GNode *parent,
+                     GNode *sibling,
+                     GNode *node)
+{
+  g_return_if_fail (parent != NULL);
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (G_NODE_IS_ROOT (node));
+  if (sibling)
+    g_return_if_fail (sibling->parent == parent);
+  
+  node->parent = parent;
+  
+  if (sibling)
+    {
+      if (sibling->prev)
+       {
+         node->prev = sibling->prev;
+         node->prev->next = node;
+         node->next = sibling;
+         sibling->prev = node;
+       }
+      else
+       {
+         node->parent->children = node;
+         node->next = sibling;
+         sibling->prev = node;
+       }
+    }
+  else
+    {
+      if (parent->children)
+       {
+         sibling = parent->children;
+         while (sibling->next)
+           sibling = sibling->next;
+         node->prev = sibling;
+         sibling->next = node;
+       }
+      else
+       node->parent->children = node;
+    }
+}
+
+void
+g_node_prepend (GNode *parent,
+               GNode *node)
+{
+  g_return_if_fail (parent != NULL);
+  
+  g_node_insert_before (parent, parent->children, node);
+}
+
+GNode*
+g_node_get_root (GNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  while (node->parent)
+    node = node->parent;
+  
+  return node;
+}
+
+gboolean
+g_node_is_ancestor (GNode *node,
+                   GNode *descendant)
+{
+  g_return_val_if_fail (node != NULL, FALSE);
+  g_return_val_if_fail (descendant != NULL, FALSE);
+  
+  while (descendant)
+    {
+      if (descendant->parent == node)
+       return TRUE;
+      
+      descendant = descendant->parent;
+    }
+  
+  return FALSE;
+}
+
+/* returns 1 for root, 2 for first level children,
+ * 3 for children's children...
+ */
+guint
+g_node_depth (GNode *node)
+{
+  register guint depth = 0;
+  
+  while (node)
+    {
+      depth++;
+      node = node->parent;
+    }
+  
+  return depth;
+}
+
+void
+g_node_reverse_children (GNode *node)
+{
+  GNode *child;
+  GNode *last;
+  
+  g_return_if_fail (node != NULL);
+  
+  child = node->children;
+  last = NULL;
+  while (child)
+    {
+      last = child;
+      child = last->next;
+      last->next = last->prev;
+      last->prev = child;
+    }
+  node->children = last;
+}
+
+guint
+g_node_max_height (GNode *root)
+{
+  register GNode *child;
+  register guint max_height = 0;
+  
+  if (!root)
+    return 0;
+  
+  child = root->children;
+  while (child)
+    {
+      register guint tmp_height;
+      
+      tmp_height = g_node_max_height (child);
+      if (tmp_height > max_height)
+       max_height = tmp_height;
+      child = child->next;
+    }
+  
+  return max_height + 1;
+}
+
+static gboolean
+g_node_traverse_pre_order (GNode           *node,
+                          GTraverseFlags    flags,
+                          GNodeTraverseFunc func,
+                          gpointer          data)
+{
+  if (node->children)
+    {
+      GNode *child;
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+      child = node->children;
+      while (child)
+       {
+         register GNode *current;
+         
+         current = child;
+         child = current->next;
+         if (g_node_traverse_pre_order (current, flags, func, data))
+           return TRUE;
+       }
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_depth_traverse_pre_order (GNode           *node,
+                                GTraverseFlags    flags,
+                                guint             depth,
+                                GNodeTraverseFunc func,
+                                gpointer          data)
+{
+  if (node->children)
+    {
+      GNode *child;
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+      depth--;
+      if (!depth)
+       return FALSE;
+      
+      child = node->children;
+      while (child)
+       {
+         register GNode *current;
+         
+         current = child;
+         child = current->next;
+         if (g_node_depth_traverse_pre_order (current, flags, depth, func, data))
+           return TRUE;
+       }
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_traverse_post_order (GNode           *node,
+                           GTraverseFlags    flags,
+                           GNodeTraverseFunc func,
+                           gpointer          data)
+{
+  if (node->children)
+    {
+      GNode *child;
+      
+      child = node->children;
+      while (child)
+       {
+         register GNode *current;
+         
+         current = child;
+         child = current->next;
+         if (g_node_traverse_post_order (current, flags, func, data))
+           return TRUE;
+       }
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_depth_traverse_post_order (GNode                   *node,
+                                 GTraverseFlags    flags,
+                                 guint             depth,
+                                 GNodeTraverseFunc func,
+                                 gpointer          data)
+{
+  if (node->children)
+    {
+      depth--;
+      if (depth)
+       {
+         GNode *child;
+         
+         child = node->children;
+         while (child)
+           {
+             register GNode *current;
+             
+             current = child;
+             child = current->next;
+             if (g_node_depth_traverse_post_order (current, flags, depth, func, data))
+               return TRUE;
+           }
+       }
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_traverse_in_order (GNode                   *node,
+                         GTraverseFlags    flags,
+                         GNodeTraverseFunc func,
+                         gpointer          data)
+{
+  if (node->children)
+    {
+      GNode *child;
+      register GNode *current;
+      
+      child = node->children;
+      current = child;
+      child = current->next;
+      
+      if (g_node_traverse_in_order (current, flags, func, data))
+       return TRUE;
+      
+      if ((flags & G_TRAVERSE_NON_LEAFS) &&
+         func (node, data))
+       return TRUE;
+      
+      while (child)
+       {
+         current = child;
+         child = current->next;
+         if (g_node_traverse_in_order (current, flags, func, data))
+           return TRUE;
+       }
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_depth_traverse_in_order (GNode           *node,
+                               GTraverseFlags    flags,
+                               guint             depth,
+                               GNodeTraverseFunc func,
+                               gpointer          data)
+{
+  if (node->children)
+    {
+      depth--;
+      if (depth)
+       {
+         GNode *child;
+         register GNode *current;
+         
+         child = node->children;
+         current = child;
+         child = current->next;
+         
+         if (g_node_depth_traverse_in_order (current, flags, depth, func, data))
+           return TRUE;
+         
+         if ((flags & G_TRAVERSE_NON_LEAFS) &&
+             func (node, data))
+           return TRUE;
+         
+         while (child)
+           {
+             current = child;
+             child = current->next;
+             if (g_node_depth_traverse_in_order (current, flags, depth, func, data))
+               return TRUE;
+           }
+       }
+      else if ((flags & G_TRAVERSE_NON_LEAFS) &&
+              func (node, data))
+       return TRUE;
+    }
+  else if ((flags & G_TRAVERSE_LEAFS) &&
+          func (node, data))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_traverse_children (GNode                    *node,
+                         GTraverseFlags     flags,
+                         GNodeTraverseFunc  func,
+                         gpointer           data)
+{
+  GNode *child;
+  
+  child = node->children;
+  
+  while (child)
+    {
+      register GNode *current;
+      
+      current = child;
+      child = current->next;
+      
+      if (current->children)
+       {
+         if ((flags & G_TRAVERSE_NON_LEAFS) &&
+             func (current, data))
+           return TRUE;
+       }
+      else if ((flags & G_TRAVERSE_LEAFS) &&
+              func (current, data))
+       return TRUE;
+    }
+  
+  child = node->children;
+  
+  while (child)
+    {
+      register GNode *current;
+      
+      current = child;
+      child = current->next;
+      
+      if (current->children &&
+         g_node_traverse_children (current, flags, func, data))
+       return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+g_node_depth_traverse_children (GNode           *node,
+                               GTraverseFlags    flags,
+                               guint             depth,
+                               GNodeTraverseFunc func,
+                               gpointer          data)
+{
+  GNode *child;
+  
+  child = node->children;
+  
+  while (child)
+    {
+      register GNode *current;
+      
+      current = child;
+      child = current->next;
+      
+      if (current->children)
+       {
+         if ((flags & G_TRAVERSE_NON_LEAFS) &&
+             func (current, data))
+           return TRUE;
+       }
+      else if ((flags & G_TRAVERSE_LEAFS) &&
+              func (current, data))
+       return TRUE;
+    }
+  
+  depth--;
+  if (!depth)
+    return FALSE;
+  
+  child = node->children;
+  
+  while (child)
+    {
+      register GNode *current;
+      
+      current = child;
+      child = current->next;
+      
+      if (current->children &&
+         g_node_depth_traverse_children (current, flags, depth, func, data))
+       return TRUE;
+    }
+  
+  return FALSE;
+}
+
+void
+g_node_traverse (GNode           *root,
+                GTraverseType     order,
+                GTraverseFlags    flags,
+                gint              depth,
+                GNodeTraverseFunc func,
+                gpointer          data)
+{
+  g_return_if_fail (root != NULL);
+  g_return_if_fail (func != NULL);
+  g_return_if_fail (order <= G_LEVEL_ORDER);
+  g_return_if_fail (flags <= G_TRAVERSE_MASK);
+  g_return_if_fail (depth == -1 || depth > 0);
+  
+  switch (order)
+    {
+    case G_PRE_ORDER:
+      if (depth < 0)
+       g_node_traverse_pre_order (root, flags, func, data);
+      else
+       g_node_depth_traverse_pre_order (root, flags, depth, func, data);
+      break;
+    case G_POST_ORDER:
+      if (depth < 0)
+       g_node_traverse_post_order (root, flags, func, data);
+      else
+       g_node_depth_traverse_post_order (root, flags, depth, func, data);
+      break;
+    case G_IN_ORDER:
+      if (depth < 0)
+       g_node_traverse_in_order (root, flags, func, data);
+      else
+       g_node_depth_traverse_in_order (root, flags, depth, func, data);
+      break;
+    case G_LEVEL_ORDER:
+      if (root->children)
+       {
+         if (!((flags & G_TRAVERSE_NON_LEAFS) &&
+               func (root, data)))
+           {
+             if (depth < 0)
+               g_node_traverse_children (root, flags, func, data);
+             else
+               {
+                 depth--;
+                 if (depth)
+                   g_node_depth_traverse_children (root, flags, depth, func, data);
+               }
+           }
+       }
+      else if (flags & G_TRAVERSE_LEAFS)
+       func (root, data);
+      break;
+    }
+}
+
+static gboolean
+g_node_find_func (GNode          *node,
+                 gpointer data)
+{
+  register gpointer *d = data;
+  
+  if (*d != node->data)
+    return FALSE;
+  
+  *(++d) = node;
+  
+  return TRUE;
+}
+
+GNode*
+g_node_find (GNode            *root,
+            GTraverseType      order,
+            GTraverseFlags     flags,
+            gpointer           data)
+{
+  gpointer d[2];
+  
+  g_return_val_if_fail (root != NULL, NULL);
+  g_return_val_if_fail (order <= G_LEVEL_ORDER, NULL);
+  g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL);
+  
+  d[0] = data;
+  d[1] = NULL;
+  
+  g_node_traverse (root, order, flags, -1, g_node_find_func, d);
+  
+  return d[1];
+}
+
+static void
+g_node_count_func (GNode        *node,
+                  GTraverseFlags flags,
+                  guint         *n)
+{
+  if (node->children)
+    {
+      GNode *child;
+      
+      if (flags & G_TRAVERSE_NON_LEAFS)
+       (*n)++;
+      
+      child = node->children;
+      while (child)
+       {
+         g_node_count_func (child, flags, n);
+         child = child->next;
+       }
+    }
+  else if (flags & G_TRAVERSE_LEAFS)
+    (*n)++;
+}
+
+guint
+g_node_n_nodes (GNode        *root,
+               GTraverseFlags flags)
+{
+  guint n = 0;
+  
+  g_return_val_if_fail (root != NULL, 0);
+  g_return_val_if_fail (flags <= G_TRAVERSE_MASK, 0);
+  
+  g_node_count_func (root, flags, &n);
+  
+  return n;
+}
+
+GNode*
+g_node_last_child (GNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  node = node->children;
+  if (node)
+    while (node->next)
+      node = node->next;
+  
+  return node;
+}
+
+GNode*
+g_node_nth_child (GNode *node,
+                 guint  n)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  node = node->children;
+  if (node)
+    while ((n-- > 0) && node)
+      node = node->next;
+  
+  return node;
+}
+
+guint
+g_node_n_children (GNode *node)
+{
+  guint n = 0;
+  
+  g_return_val_if_fail (node != NULL, 0);
+  
+  node = node->children;
+  while (node)
+    {
+      n++;
+      node = node->next;
+    }
+  
+  return n;
+}
+
+GNode*
+g_node_find_child (GNode        *node,
+                  GTraverseFlags flags,
+                  gpointer       data)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL);
+  
+  node = node->children;
+  while (node)
+    {
+      if (node->data == data)
+       {
+         if (G_NODE_IS_LEAF (node))
+           {
+             if (flags & G_TRAVERSE_LEAFS)
+               return node;
+           }
+         else
+           {
+             if (flags & G_TRAVERSE_NON_LEAFS)
+               return node;
+           }
+       }
+      node = node->next;
+    }
+  
+  return NULL;
+}
+
+gint
+g_node_child_position (GNode *node,
+                      GNode *child)
+{
+  register guint n = 0;
+  
+  g_return_val_if_fail (node != NULL, -1);
+  g_return_val_if_fail (child != NULL, -1);
+  g_return_val_if_fail (child->parent == node, -1);
+  
+  node = node->children;
+  while (node)
+    {
+      if (node == child)
+       return n;
+      n++;
+      node = node->next;
+    }
+  
+  return -1;
+}
+
+gint
+g_node_child_index (GNode   *node,
+                   gpointer data)
+{
+  register guint n = 0;
+  
+  g_return_val_if_fail (node != NULL, -1);
+  
+  node = node->children;
+  while (node)
+    {
+      if (node->data == data)
+       return n;
+      n++;
+      node = node->next;
+    }
+  
+  return -1;
+}
+
+GNode*
+g_node_first_sibling (GNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  while (node->prev)
+    node = node->prev;
+  
+  return node;
+}
+
+GNode*
+g_node_last_sibling (GNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+  
+  while (node->next)
+    node = node->next;
+  
+  return node;
+}
+
+void
+g_node_children_foreach (GNode          *node,
+                        GTraverseFlags   flags,
+                        GNodeForeachFunc func,
+                        gpointer         data)
+{
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (flags <= G_TRAVERSE_MASK);
+  g_return_if_fail (func != NULL);
+  
+  node = node->children;
+  while (node)
+    {
+      register GNode *current;
+      
+      current = node;
+      node = current->next;
+      if (G_NODE_IS_LEAF (node))
+       {
+         if (flags & G_TRAVERSE_LEAFS)
+           func (current, data);
+       }
+      else
+       {
+         if (flags & G_TRAVERSE_NON_LEAFS)
+           func (current, data);
+       }
+    }
+}
diff --git a/gtree.c b/gtree.c
index 981ff39..2a3dd8f 100644 (file)
--- a/gtree.c
+++ b/gtree.c
@@ -177,6 +177,10 @@ g_tree_traverse (GTree         *tree,
     case G_POST_ORDER:
       g_tree_node_post_order (rtree->root, traverse_func, data);
       break;
+    
+    case G_LEVEL_ORDER:
+      g_warning ("g_tree_traverse(): traverse type G_LEVEL_ORDER isn't implemented.");
+      break;
     }
 }
 
index d89e763..4fd5f7b 100644 (file)
 #include "glib.h"
 
 int array[10000];
+gboolean failed = FALSE;
+
+#define        TEST(m,cond)    G_STMT_START { failed = !(cond); \
+if (failed) \
+  { if (!m) \
+      g_print ("\n(%s:%d) failed for: %s\n", __FILE__, __LINE__, ( # cond )); \
+    else \
+      g_print ("\n(%s:%d) failed for: %s: (%s)\n", __FILE__, __LINE__, ( # cond ), (gchar*)m); \
+  } \
+else \
+  g_print ("."); fflush (stdout); \
+} G_STMT_END
+
+#define        C2P(c)          ((gpointer) ((long) (c)))
+#define        P2C(p)          ((gchar) ((long) (p)))
+
+static gboolean
+node_build_string (GNode    *node,
+                  gpointer  data)
+{
+  gchar **p = data;
+  gchar *string;
+  gchar c[2] = "_";
+
+  c[0] = P2C (node->data);
+
+  string = g_strconcat (*p ? *p : "", c, NULL);
+  g_free (*p);
+  *p = string;
+
+  return FALSE;
+}
+
+static void
+g_node_test (void)
+{
+  GNode *root;
+  GNode *node;
+  GNode *node_B;
+  GNode *node_F;
+  GNode *node_G;
+  GNode *node_J;
+  guint i;
+  gchar *tstring;
+
+  g_print ("checking n-way trees: ");
+  failed = FALSE;
+
+  root = g_node_new (C2P ('A'));
+  TEST (NULL, g_node_depth (root) == 1 && g_node_max_height (root) == 1);
+
+  node_B = g_node_new (C2P ('B'));
+  g_node_append (root, node_B);
+  TEST (NULL, root->children == node_B);
+
+  g_node_append (node_B, g_node_new (C2P ('E')));
+  g_node_prepend (node_B, g_node_new (C2P ('C')));
+  g_node_insert (node_B, 1, g_node_new (C2P ('D')));
+
+  node_F = g_node_new (C2P ('F'));
+  g_node_append (root, node_F);
+  TEST (NULL, root->children->next == node_F);
+
+  node_G = g_node_new (C2P ('G'));
+  g_node_append (node_F, node_G);
+  node_J = g_node_new (C2P ('J'));
+  g_node_insert (node_G, -1, node_J);
+  g_node_insert (node_G, 42, g_node_new (C2P ('K')));
+  g_node_insert (node_G, 0, g_node_new (C2P ('H')));
+  g_node_insert (node_G, 1, g_node_new (C2P ('I')));
+
+  TEST (NULL, g_node_depth (root) == 1);
+  TEST (NULL, g_node_max_height (root) == 4);
+  TEST (NULL, g_node_depth (node_G->children->next) == 4);
+  TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_LEAFS) == 7);
+  TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_NON_LEAFS) == 4);
+  TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_ALL) == 11);
+  TEST (NULL, g_node_max_height (node_F) == 3);
+  TEST (NULL, g_node_n_children (node_G) == 4);
+  TEST (NULL, g_node_find_child (root, G_TRAVERSE_ALL, C2P ('F')) == node_F);
+  TEST (NULL, g_node_find (root, G_LEVEL_ORDER, G_TRAVERSE_NON_LEAFS, C2P ('I')) == NULL);
+  TEST (NULL, g_node_find (root, G_IN_ORDER, G_TRAVERSE_LEAFS, C2P ('J')) == node_J);
+
+  for (i = 0; i < g_node_n_children (node_B); i++)
+    {
+      node = g_node_nth_child (node_B, i);
+      TEST (NULL, P2C (node->data) == ('C' + i));
+    }
+  
+  for (i = 0; i < g_node_n_children (node_G); i++)
+    TEST (NULL, g_node_child_position (node_G, g_node_nth_child (node_G, i)) == i);
+
+  /* we have built:                    A
+   *                                 /   \
+   *                               B       F
+   *                             / | \       \
+   *                           C   D   E       G
+   *                                         / /\ \
+   *                                       H  I  J  K
+   *
+   * for in-order traversal, 'G' is considered to be the "left"
+   * child of 'F', which will cause 'F' to be the last node visited.
+   */
+
+  tstring = NULL;
+  g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "ABCDEFGHIJK") == 0);
+  g_free (tstring); tstring = NULL;
+  g_node_traverse (root, G_POST_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "CDEBHIJKGFA") == 0);
+  g_free (tstring); tstring = NULL;
+  g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "CBDEAHGIJKF") == 0);
+  g_free (tstring); tstring = NULL;
+  g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "ABFCDEGHIJK") == 0);
+  g_free (tstring); tstring = NULL;
+  
+  g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_LEAFS, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "CDEHIJK") == 0);
+  g_free (tstring); tstring = NULL;
+  g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_NON_LEAFS, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "ABFG") == 0);
+  g_free (tstring); tstring = NULL;
+
+  g_node_reverse_children (node_B);
+  g_node_reverse_children (node_G);
+
+  g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "ABFEDCGKJIH") == 0);
+  g_free (tstring); tstring = NULL;
+  
+  g_node_destroy (root);
+
+  /* allocation tests */
+
+  root = g_node_new (NULL);
+  node = root;
+
+  for (i = 0; i < 2048; i++)
+    {
+      g_node_append (node, g_node_new (NULL));
+      if ((i%5) == 4)
+       node = node->children->next;
+    }
+  TEST (NULL, g_node_max_height (root) > 100);
+  TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_ALL) == 1 + 2048);
+
+  g_node_destroy (root);
+  
+  if (!failed)
+    g_print ("ok\n");
+}
 
 void
 my_hash_callback (gpointer key,
@@ -268,7 +421,7 @@ main (int   argc,
   g_print ("ok\n");
 
 
-  g_print ("checking trees...\n");
+  g_print ("checking binary trees...\n");
 
   tree = g_tree_new (my_compare);
   i = 0;
@@ -308,6 +461,9 @@ main (int   argc,
   g_print ("ok\n");
 
 
+  /* check n-way trees */
+  g_node_test ();
+
   g_print ("checking mem chunks...");
 
   mem_chunk = g_mem_chunk_new ("test mem chunk", 50, 100, G_ALLOC_AND_FREE);
index d89e763..4fd5f7b 100644 (file)
 #include "glib.h"
 
 int array[10000];
+gboolean failed = FALSE;
+
+#define        TEST(m,cond)    G_STMT_START { failed = !(cond); \
+if (failed) \
+  { if (!m) \
+      g_print ("\n(%s:%d) failed for: %s\n", __FILE__, __LINE__, ( # cond )); \
+    else \
+      g_print ("\n(%s:%d) failed for: %s: (%s)\n", __FILE__, __LINE__, ( # cond ), (gchar*)m); \
+  } \
+else \
+  g_print ("."); fflush (stdout); \
+} G_STMT_END
+
+#define        C2P(c)          ((gpointer) ((long) (c)))
+#define        P2C(p)          ((gchar) ((long) (p)))
+
+static gboolean
+node_build_string (GNode    *node,
+                  gpointer  data)
+{
+  gchar **p = data;
+  gchar *string;
+  gchar c[2] = "_";
+
+  c[0] = P2C (node->data);
+
+  string = g_strconcat (*p ? *p : "", c, NULL);
+  g_free (*p);
+  *p = string;
+
+  return FALSE;
+}
+
+static void
+g_node_test (void)
+{
+  GNode *root;
+  GNode *node;
+  GNode *node_B;
+  GNode *node_F;
+  GNode *node_G;
+  GNode *node_J;
+  guint i;
+  gchar *tstring;
+
+  g_print ("checking n-way trees: ");
+  failed = FALSE;
+
+  root = g_node_new (C2P ('A'));
+  TEST (NULL, g_node_depth (root) == 1 && g_node_max_height (root) == 1);
+
+  node_B = g_node_new (C2P ('B'));
+  g_node_append (root, node_B);
+  TEST (NULL, root->children == node_B);
+
+  g_node_append (node_B, g_node_new (C2P ('E')));
+  g_node_prepend (node_B, g_node_new (C2P ('C')));
+  g_node_insert (node_B, 1, g_node_new (C2P ('D')));
+
+  node_F = g_node_new (C2P ('F'));
+  g_node_append (root, node_F);
+  TEST (NULL, root->children->next == node_F);
+
+  node_G = g_node_new (C2P ('G'));
+  g_node_append (node_F, node_G);
+  node_J = g_node_new (C2P ('J'));
+  g_node_insert (node_G, -1, node_J);
+  g_node_insert (node_G, 42, g_node_new (C2P ('K')));
+  g_node_insert (node_G, 0, g_node_new (C2P ('H')));
+  g_node_insert (node_G, 1, g_node_new (C2P ('I')));
+
+  TEST (NULL, g_node_depth (root) == 1);
+  TEST (NULL, g_node_max_height (root) == 4);
+  TEST (NULL, g_node_depth (node_G->children->next) == 4);
+  TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_LEAFS) == 7);
+  TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_NON_LEAFS) == 4);
+  TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_ALL) == 11);
+  TEST (NULL, g_node_max_height (node_F) == 3);
+  TEST (NULL, g_node_n_children (node_G) == 4);
+  TEST (NULL, g_node_find_child (root, G_TRAVERSE_ALL, C2P ('F')) == node_F);
+  TEST (NULL, g_node_find (root, G_LEVEL_ORDER, G_TRAVERSE_NON_LEAFS, C2P ('I')) == NULL);
+  TEST (NULL, g_node_find (root, G_IN_ORDER, G_TRAVERSE_LEAFS, C2P ('J')) == node_J);
+
+  for (i = 0; i < g_node_n_children (node_B); i++)
+    {
+      node = g_node_nth_child (node_B, i);
+      TEST (NULL, P2C (node->data) == ('C' + i));
+    }
+  
+  for (i = 0; i < g_node_n_children (node_G); i++)
+    TEST (NULL, g_node_child_position (node_G, g_node_nth_child (node_G, i)) == i);
+
+  /* we have built:                    A
+   *                                 /   \
+   *                               B       F
+   *                             / | \       \
+   *                           C   D   E       G
+   *                                         / /\ \
+   *                                       H  I  J  K
+   *
+   * for in-order traversal, 'G' is considered to be the "left"
+   * child of 'F', which will cause 'F' to be the last node visited.
+   */
+
+  tstring = NULL;
+  g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "ABCDEFGHIJK") == 0);
+  g_free (tstring); tstring = NULL;
+  g_node_traverse (root, G_POST_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "CDEBHIJKGFA") == 0);
+  g_free (tstring); tstring = NULL;
+  g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "CBDEAHGIJKF") == 0);
+  g_free (tstring); tstring = NULL;
+  g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "ABFCDEGHIJK") == 0);
+  g_free (tstring); tstring = NULL;
+  
+  g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_LEAFS, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "CDEHIJK") == 0);
+  g_free (tstring); tstring = NULL;
+  g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_NON_LEAFS, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "ABFG") == 0);
+  g_free (tstring); tstring = NULL;
+
+  g_node_reverse_children (node_B);
+  g_node_reverse_children (node_G);
+
+  g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
+  TEST (tstring, strcmp (tstring, "ABFEDCGKJIH") == 0);
+  g_free (tstring); tstring = NULL;
+  
+  g_node_destroy (root);
+
+  /* allocation tests */
+
+  root = g_node_new (NULL);
+  node = root;
+
+  for (i = 0; i < 2048; i++)
+    {
+      g_node_append (node, g_node_new (NULL));
+      if ((i%5) == 4)
+       node = node->children->next;
+    }
+  TEST (NULL, g_node_max_height (root) > 100);
+  TEST (NULL, g_node_n_nodes (root, G_TRAVERSE_ALL) == 1 + 2048);
+
+  g_node_destroy (root);
+  
+  if (!failed)
+    g_print ("ok\n");
+}
 
 void
 my_hash_callback (gpointer key,
@@ -268,7 +421,7 @@ main (int   argc,
   g_print ("ok\n");
 
 
-  g_print ("checking trees...\n");
+  g_print ("checking binary trees...\n");
 
   tree = g_tree_new (my_compare);
   i = 0;
@@ -308,6 +461,9 @@ main (int   argc,
   g_print ("ok\n");
 
 
+  /* check n-way trees */
+  g_node_test ();
+
   g_print ("checking mem chunks...");
 
   mem_chunk = g_mem_chunk_new ("test mem chunk", 50, 100, G_ALLOC_AND_FREE);