New files implementing GSequence, a list implemented using a binary tree.
authorSoren Sandmann <sandmann@daimi.au.dk>
Sat, 3 Feb 2007 23:24:50 +0000 (23:24 +0000)
committerSøren Sandmann Pedersen <ssp@src.gnome.org>
Sat, 3 Feb 2007 23:24:50 +0000 (23:24 +0000)
2007-02-03  Soren Sandmann <sandmann@daimi.au.dk>

* glib/gsequence.[ch]: New files implementing GSequence, a list
implemented using a binary tree.
* glib/glib.h, glib/glib.symbols: Update for GSequence.
* docs/reference: Add documentation for GSequence
* tests: Add sequence-test.c, a thorough test of all of
the GSequence API.

svn path=/trunk/; revision=5322

16 files changed:
ChangeLog
docs/reference/glib/glib-docs.sgml
docs/reference/glib/glib-sections.txt
docs/reference/glib/tmpl/glib-unused.sgml
docs/reference/glib/tmpl/keyfile.sgml
docs/reference/glib/tmpl/sequence.sgml [new file with mode: 0644]
docs/reference/gobject/tmpl/enumerations_flags.sgml
docs/reference/gobject/tmpl/gparamspec.sgml
docs/reference/gobject/tmpl/gtypemodule.sgml
glib/Makefile.am
glib/glib.h
glib/glib.symbols
glib/gsequence.c [new file with mode: 0644]
glib/gsequence.h [new file with mode: 0644]
tests/Makefile.am
tests/sequence-test.c [new file with mode: 0644]

index 9b14256..4bd7301 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2007-02-03  Soren Sandmann <sandmann@daimi.au.dk>
+
+       * glib/gsequence.[ch]: New files implementing GSequence, a list
+       implemented using a binary tree.
+       * glib/glib.h, glib/glib.symbols: Update for GSequence.
+       * docs/reference: Add documentation for GSequence
+       * tests: Add sequence-test.c, a thorough test of all of 
+       the GSequence API.
+
 2007-01-30  Matthias Clasen <mclasen@redhat.com>
 
        * glib/glib.symbols:
index e3e55bd..82b6302 100644 (file)
@@ -36,6 +36,7 @@
 <!ENTITY glib-Doubly-Linked-Lists SYSTEM "xml/linked_lists_double.xml">
 <!ENTITY glib-Singly-Linked-Lists SYSTEM "xml/linked_lists_single.xml">
 <!ENTITY glib-Double-ended-Queues SYSTEM "xml/queue.xml">
+<!ENTITY glib-Sequences SYSTEM "xml/sequence.xml">
 <!ENTITY glib-Trash-Stacks SYSTEM "xml/trash_stack.xml">
 <!ENTITY glib-Hash-Tables SYSTEM "xml/hash_tables.xml">
 <!ENTITY glib-Strings SYSTEM "xml/strings.xml">
@@ -163,6 +164,7 @@ synchronize their operation.
     &glib-Doubly-Linked-Lists;
     &glib-Singly-Linked-Lists;
     &glib-Double-ended-Queues;
+    &glib-Sequences;
     &glib-Trash-Stacks;
     &glib-Hash-Tables;
     &glib-Strings;
index cbbb8cd..cd6d354 100644 (file)
@@ -1823,6 +1823,60 @@ g_queue_delete_link
 </SECTION>
 
 <SECTION>
+<TITLE>Sequences</TITLE>
+<FILE>sequence</FILE>
+
+GSequence
+GSequenceIter
+GSequenceIterCompareFunc
+
+<SUBSECTION>
+g_sequence_new
+g_sequence_free
+g_sequence_get_length
+g_sequence_foreach
+g_sequence_foreach_range
+g_sequence_sort
+g_sequence_sort_iter
+
+<SUBSECTION>
+g_sequence_get_begin_iter
+g_sequence_get_end_iter
+g_sequence_get_iter_at_pos
+g_sequence_append
+g_sequence_prepend
+g_sequence_insert_before
+g_sequence_move
+g_sequence_swap
+g_sequence_insert_sorted
+g_sequence_insert_sorted_iter
+g_sequence_sort_changed
+g_sequence_sort_changed_iter
+g_sequence_remove
+g_sequence_remove_range
+g_sequence_move_range
+g_sequence_search
+g_sequence_search_iter
+
+<SUBSECTION>
+g_sequence_get
+g_sequence_set
+
+<SUBSECTION>
+g_sequence_iter_is_begin
+g_sequence_iter_is_end
+g_sequence_iter_next
+g_sequence_iter_prev
+g_sequence_iter_get_position
+g_sequence_iter_move
+g_sequence_iter_get_sequence
+
+<SUBSECTION>
+g_sequence_iter_compare
+g_sequence_range_get_midpoint
+</SECTION>
+
+<SECTION>
 <TITLE>Trash Stacks</TITLE>
 <FILE>trash_stack</FILE>
 GTrashStack
index de6993d..b089e66 100644 (file)
@@ -203,15 +203,6 @@ error domains.
 </para>
 
 
-<!-- ##### MACRO G_HAVE_GNUC_VISIBILITY ##### -->
-<para>
-This macro is defined as 1 if the the compiler supports ELF visibility 
-attributes (currently only <command>gcc</command>).
-</para>
-
-Since: 2.6
-
-
 <!-- ##### MACRO G_HOOK_DEFERRED_DESTROY ##### -->
 <para>
 
index d63ead0..a427cf0 100644 (file)
@@ -504,8 +504,6 @@ Flags which influence the parsing.
 @key_file: 
 @group_name: 
 @key: 
-@list: 
-@length: 
 
 
 <!-- ##### FUNCTION g_key_file_set_locale_string_list ##### -->
@@ -517,8 +515,6 @@ Flags which influence the parsing.
 @group_name: 
 @key: 
 @locale: 
-@list: 
-@length: 
 
 
 <!-- ##### FUNCTION g_key_file_set_boolean_list ##### -->
diff --git a/docs/reference/glib/tmpl/sequence.sgml b/docs/reference/glib/tmpl/sequence.sgml
new file mode 100644 (file)
index 0000000..325ab78
--- /dev/null
@@ -0,0 +1,433 @@
+<!-- ##### SECTION Title ##### -->
+Sequences
+
+<!-- ##### SECTION Short_Description ##### -->
+scalable lists
+
+<!-- ##### SECTION Long_Description ##### -->
+
+<para>
+The #GSequence data structure has the API of a list, but is
+implemented internally with a balanced binary tree. This means that it
+is possible to maintain a sorted list of n elements in time O(n log
+n). The data contained in each element can be either integer values, by
+using of the 
+<link linkend="glib-Type-Conversion-Macros">Type Conversion Macros</link>,
+or simply pointers to any type of data.
+</para>
+
+<para>
+A #GSequence is accessed through <firstterm>iterators</firstterm>,
+represented by a #GSequenceIter. An iterator represents a position
+between two elements of the sequence. For example, the
+<firstterm>begin</firstterm> iterator represents the gap immediately
+before the first element of the sequence, and the
+<firstterm>end</firstterm> iterator represents the gap immediately
+after the last element. In an empty sequence, the begin and end
+iterators are the same.
+</para>
+
+<para>
+Some methods on #GSequence operate on ranges of items. For example
+g_sequence_foreach_range() will call a user-specified function on each
+element with the given range. The range is delimited by the gaps
+represented by the passed-in iterators, so if you pass in the begin
+and end iterators, the range in question is the entire sequence.
+</para>
+
+
+<para>
+The function g_sequence_get() is used with an iterator to access the
+element immediately following the gap that the iterator
+represents. The iterator is said to <firstterm>point</firstterm> to
+that element.
+</para>
+
+<para>
+Iterators are stable across most operations on a #GSequence. For
+example an iterator pointing to some element of a sequence will
+continue to point to that element even after the sequence is
+sorted. Even moving an element to another sequence using for example
+g_sequence_move_range() will not invalidate the iterators pointing to
+it. The only operation that will invalidate an iterator is when the
+element it points to is removed from any sequence.
+</para>
+
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GSequence ##### -->
+<para>
+The <structname>GSequence</structname> struct is an opaque data type
+representing a <link linkend="glib-Sequences">Sequence</link> data type.
+</para>
+
+
+<!-- ##### TYPEDEF GSequenceIter ##### -->
+<para>
+The <structname>GSequenceIter</structname> struct is an opaque data
+type representing an iterator pointing into a #GSequence.
+</para>
+
+
+<!-- ##### USER_FUNCTION GSequenceIterCompareFunc ##### -->
+<para>
+A #GSequenceIterCompareFunc is a function used to compare
+iterators. It must return zero if the iterators compare equal, a
+negative value if @a comes before @b, and a positive value if @b comes
+before @a.
+</para>
+
+@a: a #GSequenceIter
+@b: a #GSequenceIter
+@data: user data
+@Returns: zero if the iterators are equal, a negative value if @a
+comes before @b, and a positive value if @b comes before @a.
+
+
+<!-- ##### FUNCTION g_sequence_new ##### -->
+<para>
+
+</para>
+
+@data_destroy: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_free ##### -->
+<para>
+
+</para>
+
+@seq: 
+
+
+<!-- ##### FUNCTION g_sequence_get_length ##### -->
+<para>
+
+</para>
+
+@seq: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_foreach ##### -->
+<para>
+
+</para>
+
+@seq: 
+@func: 
+@user_data: 
+
+
+<!-- ##### FUNCTION g_sequence_foreach_range ##### -->
+<para>
+
+</para>
+
+@begin: 
+@end: 
+@func: 
+@user_data: 
+
+
+<!-- ##### FUNCTION g_sequence_sort ##### -->
+<para>
+
+</para>
+
+@seq: 
+@cmp_func: 
+@cmp_data: 
+
+
+<!-- ##### FUNCTION g_sequence_sort_iter ##### -->
+<para>
+
+</para>
+
+@seq: 
+@cmp_func: 
+@cmp_data: 
+
+
+<!-- ##### FUNCTION g_sequence_get_begin_iter ##### -->
+<para>
+
+</para>
+
+@seq: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_get_end_iter ##### -->
+<para>
+
+</para>
+
+@seq: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_get_iter_at_pos ##### -->
+<para>
+
+</para>
+
+@seq: 
+@pos: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_append ##### -->
+<para>
+
+</para>
+
+@seq: 
+@data: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_prepend ##### -->
+<para>
+
+</para>
+
+@seq: 
+@data: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_insert_before ##### -->
+<para>
+
+</para>
+
+@iter: 
+@data: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_move ##### -->
+<para>
+
+</para>
+
+@src: 
+@dest: 
+
+
+<!-- ##### FUNCTION g_sequence_swap ##### -->
+<para>
+
+</para>
+
+@a: 
+@b: 
+
+
+<!-- ##### FUNCTION g_sequence_insert_sorted ##### -->
+<para>
+
+</para>
+
+@seq: 
+@data: 
+@cmp_func: 
+@cmp_data: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_insert_sorted_iter ##### -->
+<para>
+
+</para>
+
+@seq: 
+@data: 
+@iter_cmp: 
+@cmp_data: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_sort_changed ##### -->
+<para>
+
+</para>
+
+@iter: 
+@cmp_func: 
+@cmp_data: 
+
+
+<!-- ##### FUNCTION g_sequence_sort_changed_iter ##### -->
+<para>
+
+</para>
+
+@iter: 
+@iter_cmp: 
+@cmp_data: 
+
+
+<!-- ##### FUNCTION g_sequence_remove ##### -->
+<para>
+
+</para>
+
+@iter: 
+
+
+<!-- ##### FUNCTION g_sequence_remove_range ##### -->
+<para>
+
+</para>
+
+@begin: 
+@end: 
+
+
+<!-- ##### FUNCTION g_sequence_move_range ##### -->
+<para>
+
+</para>
+
+@dest: 
+@begin: 
+@end: 
+
+
+<!-- ##### FUNCTION g_sequence_search ##### -->
+<para>
+
+</para>
+
+@seq: 
+@data: 
+@cmp_func: 
+@cmp_data: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_search_iter ##### -->
+<para>
+
+</para>
+
+@seq: 
+@data: 
+@iter_cmp: 
+@cmp_data: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_get ##### -->
+<para>
+
+</para>
+
+@iter: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_set ##### -->
+<para>
+
+</para>
+
+@iter: 
+@data: 
+
+
+<!-- ##### FUNCTION g_sequence_iter_is_begin ##### -->
+<para>
+
+</para>
+
+@iter: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_iter_is_end ##### -->
+<para>
+
+</para>
+
+@iter: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_iter_next ##### -->
+<para>
+
+</para>
+
+@iter: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_iter_prev ##### -->
+<para>
+
+</para>
+
+@iter: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_iter_get_position ##### -->
+<para>
+
+</para>
+
+@iter: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_iter_move ##### -->
+<para>
+
+</para>
+
+@iter: 
+@delta: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_iter_get_sequence ##### -->
+<para>
+
+</para>
+
+@iter: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_iter_compare ##### -->
+<para>
+
+</para>
+
+@a: 
+@b: 
+@Returns: 
+
+
+<!-- ##### FUNCTION g_sequence_range_get_midpoint ##### -->
+<para>
+
+</para>
+
+@begin: 
+@end: 
+@Returns: 
+
+
index 2b72931..1293d2f 100644 (file)
@@ -232,11 +232,13 @@ than to write one yourself using g_enum_register_static().
 </para>
 
 @name: A nul-terminated string used as the name of the new type.
+@_static_values: 
+@Returns: The new type identifier.
+<!-- # Unused Parameters # -->
 @const_static_values: An array of #GEnumValue structs for the possible
   enumeration values. The array is terminated by a struct with all 
   members being 0. GObject keeps a reference to the data, so it cannot
   be stack-allocated.
-@Returns: The new type identifier.
 
 
 <!-- ##### FUNCTION g_flags_register_static ##### -->
@@ -250,10 +252,12 @@ than to write one yourself using g_flags_register_static().
 </para>
 
 @name: A nul-terminated string used as the name of the new type.
+@_static_values: 
+@Returns: The new type identifier.
+<!-- # Unused Parameters # -->
 @const_static_values: An array of #GFlagsValue structs for the possible
   flags values. The array is terminated by a struct with all members being 0.
   GObject keeps a reference to the data, so it cannot be stack-allocated.
-@Returns: The new type identifier.
 
 
 <!-- ##### FUNCTION g_enum_complete_type_info ##### -->
@@ -282,6 +286,8 @@ my_enum_complete_type_info (GTypePlugin     *plugin,
 
 @g_enum_type: the type identifier of the type being completed
 @info: the #GTypeInfo struct to be filled in
+@_values: 
+<!-- # Unused Parameters # -->
 @const_values: An array of #GEnumValue structs for the possible
   enumeration values. The array is terminated by a struct with all 
   members being 0.
@@ -296,6 +302,8 @@ g_enumeration_complete_type_info() above.
 
 @g_flags_type: the type identifier of the type being completed
 @info: the #GTypeInfo struct to be filled in
+@_values: 
+<!-- # Unused Parameters # -->
 @const_values: An array of #GFlagsValue structs for the possible
   enumeration values. The array is terminated by a struct with all 
   members being 0.
index eb90ffd..ceb5803 100644 (file)
@@ -149,10 +149,6 @@ can be configured.
                          parameter is guaranteed to remain valid and
                          unmodified for the lifetime of the parameter. 
                          Since 2.8
-@G_PARAM_STATIC_NICK:    the string used as nick when constructing the
-                         parameter is guaranteed to remain valid and
-                         unmodified for the lifetime of the parameter.
-                         Since 2.8
 @G_PARAM_STATIC_BLURB:   the string used as blurb when constructing the 
                          parameter is guaranteed to remain valid and 
                          unmodified for the lifetime of the parameter. 
index 71ce8f1..3b2e9ee 100644 (file)
@@ -164,11 +164,13 @@ not be unloaded.
 
 @module:              a #GTypeModule
 @name:                name for the type
+@_static_values: 
+@Returns: the new or existing type ID
+@Since: 2.6
+<!-- # Unused Parameters # -->
 @const_static_values: an array of #GEnumValue structs for the possible
   enumeration values. The array is terminated by a struct with all 
   members being 0.
-@Returns: the new or existing type ID
-@Since: 2.6
 
 
 <!-- ##### FUNCTION g_type_module_register_flags ##### -->
@@ -185,11 +187,13 @@ not be unloaded.
 
 @module:              a #GTypeModule
 @name:                name for the type
+@_static_values: 
+@Returns: the new or existing type ID
+@Since: 2.6
+<!-- # Unused Parameters # -->
 @const_static_values: an array of #GFlagsValue structs for the possible
   flags values. The array is terminated by a struct with all 
   members being 0.
-@Returns: the new or existing type ID
-@Since: 2.6
 
 
 <!-- ##### MACRO G_DEFINE_DYNAMIC_TYPE ##### -->
index 1ea7a0f..4d3b2ad 100644 (file)
@@ -108,6 +108,7 @@ libglib_2_0_la_SOURCES =    \
        grand.c                 \
        gscanner.c              \
        gscripttable.h          \
+       gsequence.c             \
        gshell.c                \
        gslice.c                \
        gslist.c                \
@@ -186,6 +187,7 @@ glibsubinclude_HEADERS =   \
        grand.h         \
        grel.h          \
        gscanner.h      \
+       gsequence.h     \
        gshell.h        \
        gslice.h        \
        gslist.h        \
index e5044f9..f3395aa 100644 (file)
@@ -63,6 +63,7 @@
 #include <glib/grand.h>
 #include <glib/grel.h>
 #include <glib/gscanner.h>
+#include <glib/gsequence.h>
 #include <glib/gshell.h>
 #include <glib/gslist.h>
 #include <glib/gspawn.h>
index ef1adc6..0340215 100644 (file)
@@ -924,6 +924,46 @@ g_scanner_warn G_GNUC_PRINTF(2,3)
 #endif
 #endif
 
+#if IN_HEADER(__G_SEQUENCE_H__)
+#if IN_FILE(__G_SEQUENCE_C__)
+g_sequence_new
+g_sequence_free
+g_sequence_get_length
+g_sequence_foreach
+g_sequence_foreach_range
+g_sequence_sort
+g_sequence_sort_iter
+g_sequence_get_begin_iter
+g_sequence_get_end_iter
+g_sequence_get_iter_at_pos
+g_sequence_append
+g_sequence_prepend
+g_sequence_insert_before
+g_sequence_move
+g_sequence_swap
+g_sequence_insert_sorted
+g_sequence_insert_sorted_iter
+g_sequence_sort_changed
+g_sequence_sort_changed_iter
+g_sequence_remove
+g_sequence_remove_range
+g_sequence_move_range
+g_sequence_search
+g_sequence_search_iter
+g_sequence_get
+g_sequence_set
+g_sequence_iter_is_begin
+g_sequence_iter_is_end
+g_sequence_iter_next
+g_sequence_iter_prev
+g_sequence_iter_get_position
+g_sequence_iter_move
+g_sequence_iter_get_sequence
+g_sequence_iter_compare
+g_sequence_range_get_midpoint
+#endif
+#endif
+
 #if IN_HEADER(__G_SHELL_H__)
 #if IN_FILE(__G_SHELL_C__)
 g_shell_error_quark
diff --git a/glib/gsequence.c b/glib/gsequence.c
new file mode 100644 (file)
index 0000000..5ecd464
--- /dev/null
@@ -0,0 +1,1747 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
+ * Soeren Sandmann (sandmann@daimi.au.dk)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "config.h"
+
+#include "glib.h"
+#include "galias.h"
+
+typedef struct _GSequenceNode GSequenceNode;
+
+struct _GSequence
+{
+  GSequenceNode *       end_node;
+  GDestroyNotify        data_destroy_notify;
+  gboolean              access_prohibited;
+};
+
+struct _GSequenceNode
+{
+  gint                  n_nodes;
+  GSequenceNode *       parent;    
+  GSequenceNode *       left;
+  GSequenceNode *       right;
+  gpointer              data;   /* For the end node, this field points
+                                 * to the sequence
+                                 */
+};
+
+/*
+ * Declaration of GSequenceNode methods
+ */
+static GSequenceNode *node_new           (gpointer                  data);
+static GSequenceNode *node_get_first     (GSequenceNode            *node);
+static GSequenceNode *node_get_last      (GSequenceNode            *node);
+static GSequenceNode *node_get_prev      (GSequenceNode            *node);
+static GSequenceNode *node_get_next      (GSequenceNode            *node);
+static gint           node_get_pos       (GSequenceNode            *node);
+static GSequenceNode *node_get_by_pos    (GSequenceNode            *node,
+                                          gint                      pos);
+static GSequenceNode *node_find_closest  (GSequenceNode            *haystack,
+                                          GSequenceNode            *needle,
+                                          GSequenceNode            *end,
+                                          GSequenceIterCompareFunc  cmp,
+                                          gpointer                  user_data);
+static gint           node_get_length    (GSequenceNode            *node);
+static void           node_free          (GSequenceNode            *node,
+                                          GSequence                *seq);
+static void           node_cut           (GSequenceNode            *split);
+static void           node_insert_after  (GSequenceNode            *node,
+                                          GSequenceNode            *second);
+static void           node_insert_before (GSequenceNode            *node,
+                                          GSequenceNode            *new);
+static void           node_unlink        (GSequenceNode            *node);
+static void           node_insert_sorted (GSequenceNode            *node,
+                                          GSequenceNode            *new,
+                                          GSequenceNode            *end,
+                                          GSequenceIterCompareFunc  cmp_func,
+                                          gpointer                  cmp_data);
+
+/*
+ * Various helper functions
+ */
+static void
+check_seq_access (GSequence *seq)
+{
+  if (G_UNLIKELY (seq->access_prohibited))
+    {
+      g_warning ("Accessing a sequence while it is "
+                 "being sorted or searched is not allowed");
+    }
+}
+
+static GSequence *
+get_sequence (GSequenceNode *node)
+{
+  return (GSequence *)node_get_last (node)->data;
+}
+
+static void
+check_iter_access (GSequenceIter *iter)
+{
+  check_seq_access (get_sequence (iter));
+}
+
+static gboolean
+is_end (GSequenceIter *iter)
+{
+  GSequence *seq = get_sequence (iter);
+  
+  return seq->end_node == iter;
+}
+
+typedef struct
+{
+  GCompareDataFunc  cmp_func;
+  gpointer          cmp_data;
+  GSequenceNode    *end_node;
+} SortInfo;
+
+/* This function compares two iters using a normal compare
+ * function and user_data passed in in a SortInfo struct
+ */
+static gint
+iter_compare (GSequenceIter *node1,
+              GSequenceIter *node2,
+              gpointer       data)
+{
+  const SortInfo *info = data;
+  gint retval;
+  
+  if (node1 == info->end_node)
+    return 1;
+  
+  if (node2 == info->end_node)
+    return -1;
+  
+  retval = info->cmp_func (node1->data, node2->data, info->cmp_data);
+  
+  return retval;
+}
+
+/*
+ * Public API
+ */
+
+/**
+ * g_sequence_new:
+ * @data_destroy: a #GDestroyNotify function, or %NULL
+ * 
+ * Creates a new GSequence. The @data_destroy function, if non-%NULL will
+ * be called on all items when the sequence is destroyed and on items that
+ * are removed from the sequence.
+ * 
+ * Return value: a new #GSequence
+ * 
+ * Since: 2.14
+ **/
+GSequence *
+g_sequence_new (GDestroyNotify data_destroy)
+{
+  GSequence *seq = g_new (GSequence, 1);
+  seq->data_destroy_notify = data_destroy;
+  
+  seq->end_node = node_new (seq);
+  
+  seq->access_prohibited = FALSE;
+  
+  return seq;
+}
+
+/**
+ * g_sequence_free:
+ * @seq: a #GSequence
+ * 
+ * Frees the memory allocated for @seq. If @seq has a data destroy 
+ * function associated with it, that function is called on all items in
+ * @seq.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_free (GSequence *seq)
+{
+  g_return_if_fail (seq != NULL);
+  
+  check_seq_access (seq);
+  
+  node_free (seq->end_node, seq);
+  
+  g_free (seq);
+}
+
+/**
+ * g_sequence_foreach_range:
+ * @begin: a #GSequenceIter
+ * @end: a #GSequenceIter
+ * @func: a #GFunc
+ * @user_data: user data passed to @func
+ * 
+ * Calls @func for each item in the range (@begin, @end) passing
+ * @user_data to the function.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_foreach_range (GSequenceIter *begin,
+                          GSequenceIter *end,
+                          GFunc          func,
+                          gpointer       user_data)
+{
+  GSequence *seq;
+  GSequenceIter *iter;
+  
+  g_return_if_fail (func != NULL);
+  g_return_if_fail (begin != NULL);
+  g_return_if_fail (end != NULL);
+  
+  seq = get_sequence (begin);
+  
+  seq->access_prohibited = TRUE;
+  
+  iter = begin;
+  while (iter != end)
+    {
+      GSequenceIter *next = node_get_next (iter);
+      
+      func (iter->data, user_data);
+      
+      iter = next;
+    }
+  
+  seq->access_prohibited = FALSE;
+}
+
+/**
+ * g_sequence_foreach:
+ * @seq: a #GSequence
+ * @func: the function to call for each item in @seq
+ * @user_data: user data passed to @func
+ * 
+ * Calls @func for each item in the sequence passing @user_data
+ * to the function.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_foreach (GSequence *seq,
+                    GFunc      func,
+                    gpointer   user_data)
+{
+  GSequenceIter *begin, *end;
+  
+  check_seq_access (seq);
+  
+  begin = g_sequence_get_begin_iter (seq);
+  end   = g_sequence_get_end_iter (seq);
+  
+  g_sequence_foreach_range (begin, end, func, user_data);
+}
+
+/**
+ * g_sequence_range_get_midpoint:
+ * @begin: a #GSequenceIter
+ * @end: a #GSequenceIter
+ * 
+ * Finds an iterator somewhere in the range (@begin, @end). This
+ * iterator will be close to the middle of the range, but is not
+ * guaranteed to be <emphasis>exactly</emphasis> in the middle.
+ *
+ * The @begin and @end iterators must both point to the same sequence and
+ * @begin must come before or be equal to @end in the sequence.
+ * 
+ * Return value: A #GSequenceIter pointing somewhere in the
+ * (@begin, @end) range.
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_range_get_midpoint (GSequenceIter *begin,
+                               GSequenceIter *end)
+{
+  int begin_pos, end_pos, mid_pos;
+  
+  g_return_val_if_fail (begin != NULL, NULL);
+  g_return_val_if_fail (end != NULL, NULL);
+  g_return_val_if_fail (get_sequence (begin) == get_sequence (end), NULL);
+  
+  begin_pos = node_get_pos (begin);
+  end_pos = node_get_pos (end);
+  
+  g_return_val_if_fail (end_pos >= begin_pos, NULL);
+  
+  mid_pos = begin_pos + (end_pos - begin_pos) / 2;
+  
+  return node_get_by_pos (begin, mid_pos);
+}
+
+/**
+ * g_sequence_iter_compare:
+ * @a: a #GSequenceIter
+ * @b: a #GSequenceIter
+ * 
+ * Returns a negative number if @a comes before @b, 0 if they are equal,
+ * and a positive number if @a comes after @b.
+ *
+ * The @a and @b iterators must point into the same sequence.
+ * 
+ * Return value: A negative number if @a comes before @b, 0 if they are
+ * equal, and a positive number if @a comes after @b.
+ * 
+ * Since: 2.14
+ **/
+gint
+g_sequence_iter_compare (GSequenceIter *a,
+                         GSequenceIter *b)
+{
+  gint a_pos, b_pos;
+  
+  g_return_val_if_fail (a != NULL, 0);
+  g_return_val_if_fail (b != NULL, 0);
+  g_return_val_if_fail (get_sequence (a) == get_sequence (b), 0);
+  
+  check_iter_access (a);
+  check_iter_access (b);
+  
+  a_pos = node_get_pos (a);
+  b_pos = node_get_pos (b);
+  
+  if (a_pos == b_pos)
+    return 0;
+  else if (a_pos > b_pos)
+    return 1;
+  else
+    return -1;
+}
+
+/**
+ * g_sequence_append:
+ * @seq: a #GSequencePointer
+ * @data: the data for the new item
+ * 
+ * Adds a new item to the end of @seq.
+ * 
+ * Return value: an iterator pointing to the new item
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_append (GSequence *seq,
+                   gpointer   data)
+{
+  GSequenceNode *node;
+  
+  g_return_val_if_fail (seq != NULL, NULL);
+  
+  check_seq_access (seq);
+  
+  node = node_new (data);
+  node_insert_before (seq->end_node, node);
+  
+  return node;
+}
+
+/**
+ * g_sequence_prepend:
+ * @seq: a #GSequence
+ * @data: the data for the new item
+ * 
+ * Adds a new item to the front of @seq
+ * 
+ * Return value: an iterator pointing to the new item
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_prepend (GSequence *seq,
+                    gpointer   data)
+{
+  GSequenceNode *node, *first;
+  
+  g_return_val_if_fail (seq != NULL, NULL);
+  
+  check_seq_access (seq);
+  
+  node = node_new (data);
+  first = node_get_first (seq->end_node);
+  
+  node_insert_before (first, node);
+  
+  return node;
+}
+
+/**
+ * g_sequence_insert_before:
+ * @iter: a #GSequenceIter
+ * @data: the data for the new item
+ * 
+ * Inserts a new item just before the item pointed to by @iter.
+ * 
+ * Return value: an iterator pointing to the new item
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_insert_before (GSequenceIter *iter,
+                          gpointer       data)
+{
+  GSequenceNode *node;
+  
+  g_return_val_if_fail (iter != NULL, NULL);
+  
+  check_iter_access (iter);
+  
+  node = node_new (data);
+  
+  node_insert_before (iter, node);
+  
+  return node;
+}
+
+/**
+ * g_sequence_remove:
+ * @iter: a #GSequenceIter
+ * 
+ * Removes the item pointed to by @iter. It is an error to pass the
+ * end iterator to this function.
+ *
+ * If the sequnce has a data destroy function associated with it, this
+ * function is called on the data for the removed item.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_remove (GSequenceIter *iter)
+{
+  GSequence *seq;
+  
+  g_return_if_fail (iter != NULL);
+  g_return_if_fail (!is_end (iter));
+  
+  check_iter_access (iter);
+  
+  seq = get_sequence (iter); 
+  
+  node_unlink (iter);
+  node_free (iter, seq);
+}
+
+/**
+ * g_sequence_remove_range:
+ * @begin: a #GSequenceIter
+ * @end: a #GSequenceIter
+ * 
+ * Removes all items in the (@begin, @end) range.
+ *
+ * If the sequence has a data destroy function associated with it, this
+ * function is called on the data for the removed items.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_remove_range (GSequenceIter *begin,
+                         GSequenceIter *end)
+{
+  g_return_if_fail (get_sequence (begin) == get_sequence (end));
+  
+  check_iter_access (begin);
+  check_iter_access (end);
+  
+  g_sequence_move_range (NULL, begin, end);
+}
+
+/**
+ * g_sequence_move_range:
+ * @dest: a #GSequenceIter
+ * @begin: a #GSequenceIter
+ * @end: a #GSequenceIter
+ * 
+ * Inserts the (@begin, @end) range at the destination pointed to by ptr.
+ * The @begin and @end iters must point into the same sequence. It is
+ * allowed for @dest to point to a different sequence than the one pointed
+ * into by @begin and @end.
+ * 
+ * If @dest is NULL, the range indicated by @begin and @end is
+ * removed from the sequence. If @dest iter points to a place within
+ * the (@begin, @end) range, the range does not move.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_move_range (GSequenceIter *dest,
+                       GSequenceIter *begin,
+                       GSequenceIter *end)
+{
+  GSequence *src_seq;
+  GSequenceNode *first;
+  
+  g_return_if_fail (begin != NULL);
+  g_return_if_fail (end != NULL);
+  
+  check_iter_access (begin);
+  check_iter_access (end);
+  if (dest)
+    check_iter_access (dest);
+  
+  src_seq = get_sequence (begin);
+  
+  g_return_if_fail (src_seq == get_sequence (end));
+  
+  /* Dest points to begin or end? */
+  if (dest == begin || dest == end)
+    return;
+  
+  /* begin comes after end? */
+  if (g_sequence_iter_compare (begin, end) >= 0)
+    return;
+  
+  /* dest points somewhere in the (begin, end) range? */
+  if (dest && get_sequence (dest) == src_seq &&
+      g_sequence_iter_compare (dest, begin) > 0 &&
+      g_sequence_iter_compare (dest, end) < 0)
+    {
+      return;
+    }
+  
+  src_seq = get_sequence (begin);
+  
+  first = node_get_first (begin);
+  
+  node_cut (begin);
+  
+  node_cut (end);
+  
+  if (first != begin)
+    node_insert_after (node_get_last (first), end);
+  
+  if (dest)
+    node_insert_before (dest, begin);
+  else
+    node_free (begin, src_seq);
+}
+
+/**
+ * g_sequence_sort:
+ * @seq: a #GSequence
+ * @cmp_func: the #GCompareDataFunc used to sort @seq. This function is
+ *       passed two items of @seq and should return 0 if they are equal,
+ *       a negative value fi the first comes before the second, and a
+ *       positive value if the second comes before the first.
+ * @cmp_data: user data passed to @cmp_func
+ * 
+ * Sorts @seq using @cmp_func.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_sort (GSequence        *seq,
+                 GCompareDataFunc  cmp_func,
+                 gpointer          cmp_data)
+{
+  SortInfo info = { cmp_func, cmp_data, seq->end_node };
+  
+  check_seq_access (seq);
+  
+  g_sequence_sort_iter (seq, iter_compare, &info);
+}
+
+/**
+ * g_sequence_insert_sorted:
+ * @seq: a #GSequence
+ * @data: the data to insert
+ * @cmp_func: the #GCompareDataFunc used to compare items in the sequence. It
+ *     is called with two items of the @seq and @user_data. It should
+ *     return 0 if the items are equal, a negative value if the first
+ *     item comes before the second, and a positive value if the second
+ *     item comes before the first.
+ * @cmp_data: user data passed to @cmp_func.
+ *
+ * Inserts @data into @sequence using @func to determine the new position.
+ * The sequence must already be sorted according to @cmp_func; otherwise the
+ * new position of @data is undefined.
+ *
+ * Return value: a #GSequenceIter pointing to the new item.
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_insert_sorted (GSequence        *seq,
+                          gpointer          data,
+                          GCompareDataFunc  cmp_func,
+                          gpointer          cmp_data)
+{
+  SortInfo info = { cmp_func, cmp_data, NULL };
+  
+  g_return_val_if_fail (seq != NULL, NULL);
+  g_return_val_if_fail (cmp_func != NULL, NULL);
+  
+  info.end_node = seq->end_node;
+  check_seq_access (seq);
+  
+  return g_sequence_insert_sorted_iter (seq, data, iter_compare, &info);
+}
+
+/**
+ * g_sequence_sort_changed:
+ * @iter: A #GSequenceIter
+ * @cmp_func: the #GCompareDataFunc used to compare items in the sequence. It
+ *     is called with two items of the @seq and @user_data. It should
+ *     return 0 if the items are equal, a negative value if the first
+ *     item comes before the second, and a positive value if the second
+ *     item comes before the first.
+ * @cmp_data: user data passed to @cmp_func.
+ *
+ * Moves the data pointed to a new position as indicated by @cmp_func. This
+ * function should be called for items in a sequence already sorted according
+ * to @cmp_func whenever some aspect of an item changes so that @cmp_func
+ * may return different values for that item.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_sort_changed (GSequenceIter    *iter,
+                         GCompareDataFunc  cmp_func,
+                         gpointer          cmp_data)
+{
+  SortInfo info = { cmp_func, cmp_data, NULL };
+  
+  g_return_if_fail (!is_end (iter));
+  
+  info.end_node = get_sequence (iter)->end_node;
+  check_iter_access (iter);
+  
+  g_sequence_sort_changed_iter (iter, iter_compare, &info);
+}
+
+/**
+ * g_sequence_search:
+ * @seq: a #GSequence
+ * @data: data for the new item
+ * @cmp_func: the #GCompareDataFunc used to compare items in the sequence. It
+ *     is called with two items of the @seq and @user_data. It should
+ *     return 0 if the items are equal, a negative value if the first
+ *     item comes before the second, and a positive value if the second
+ *     item comes before the first.
+ * @cmp_data: user data passed to @cmp_func.
+ * 
+ * Returns an iterator pointing to the position where @data would
+ * be inserted according to @cmp_func and @cmp_data.
+ * 
+ * Return value: an #GSequenceIter pointing to the position where @data
+ * would have been inserted according to @cmp_func and @cmp_data.
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_search (GSequence        *seq,
+                   gpointer          data,
+                   GCompareDataFunc  cmp_func,
+                   gpointer          cmp_data)
+{
+  SortInfo info = { cmp_func, cmp_data, NULL };
+  
+  g_return_val_if_fail (seq != NULL, NULL);
+  
+  info.end_node = seq->end_node;
+  check_seq_access (seq);
+  
+  return g_sequence_search_iter (seq, data, iter_compare, &info);
+}
+
+/**
+ * g_sequence_sort_iter:
+ * @seq: a #GSequence
+ * @cmp_func: the #GSequenceItercompare used to compare iterators in the
+ *     sequence. It is called with two iterators pointing into @seq. It should
+ *     return 0 if the iterators are equal, a negative value if the first
+ *     iterator comes before the second, and a positive value if the second
+ *     iterator comes before the first.
+ * @cmp_data: user data passed to @cmp_func
+ *
+ * Like g_sequence_sort(), but uses a #GSequenceIterCompareFunc instead
+ * of a GCompareDataFunc as the compare function
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_sort_iter (GSequence                *seq,
+                      GSequenceIterCompareFunc  cmp_func,
+                      gpointer                  cmp_data)
+{
+  GSequence *tmp;
+  GSequenceNode *begin, *end;
+  
+  g_return_if_fail (seq != NULL);
+  g_return_if_fail (cmp_func != NULL);
+  
+  check_seq_access (seq);
+  
+  begin = g_sequence_get_begin_iter (seq);
+  end   = g_sequence_get_end_iter (seq);
+  
+  tmp = g_sequence_new (NULL);
+  
+  g_sequence_move_range (g_sequence_get_begin_iter (tmp), begin, end);
+  
+  tmp->access_prohibited = TRUE;
+  seq->access_prohibited = TRUE;
+  
+  while (g_sequence_get_length (tmp) > 0)
+    {
+      GSequenceNode *node = g_sequence_get_begin_iter (tmp);
+      
+      node_insert_sorted (seq->end_node, node, seq->end_node,
+                         cmp_func, cmp_data);
+    }
+  
+  tmp->access_prohibited = FALSE;
+  seq->access_prohibited = FALSE;
+  
+  g_sequence_free (tmp);
+}
+
+/**
+ * g_sequence_sort_changed_iter:
+ * @iter: a #GSequenceIter
+ * @iter_cmp: the #GSequenceItercompare used to compare iterators in the
+ *     sequence. It is called with two iterators pointing into @seq. It should
+ *     return 0 if the iterators are equal, a negative value if the first
+ *     iterator comes before the second, and a positive value if the second
+ *     iterator comes before the first.
+ * @cmp_data: user data passed to @cmp_func
+ *
+ * Like g_sequence_sort_changed(), but uses
+ * a #GSequenceIterCompareFunc instead of a #GCompareDataFunc as
+ * the compare function.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_sort_changed_iter (GSequenceIter            *iter,
+                              GSequenceIterCompareFunc  iter_cmp,
+                              gpointer                  cmp_data)
+{
+  GSequence *seq, *tmp_seq;
+  GSequenceIter *next, *prev;
+
+  g_return_if_fail (iter != NULL);
+  g_return_if_fail (!is_end (iter));
+  g_return_if_fail (iter_cmp != NULL);
+  check_iter_access (iter);
+  
+  /* If one of the neighbours is equal to iter, then
+   * don't move it. This ensures that sort_changed() is
+   * a stable operation.
+   */
+  
+  next = node_get_next (iter);
+  prev = node_get_prev (iter);
+  
+  if (prev != iter && iter_cmp (prev, iter, cmp_data) == 0)
+    return;
+  
+  if (!is_end (next) && iter_cmp (next, iter, cmp_data) == 0)
+    return;
+  
+  seq = get_sequence (iter);
+  
+  seq->access_prohibited = TRUE;
+
+  tmp_seq = g_sequence_new (NULL);
+  node_unlink (iter);
+  node_insert_before (tmp_seq->end_node, iter);
+  
+  node_insert_sorted (seq->end_node, iter, seq->end_node,
+                      iter_cmp, cmp_data);
+
+  g_sequence_free (tmp_seq);
+  
+  seq->access_prohibited = FALSE;
+}
+
+/**
+ * g_sequence_insert_sorted_iter:
+ * @seq: a #GSequence
+ * @data: data for the new item
+ * @iter_cmp: the #GSequenceItercompare used to compare iterators in the
+ *     sequence. It is called with two iterators pointing into @seq. It should
+ *     return 0 if the iterators are equal, a negative value if the first
+ *     iterator comes before the second, and a positive value if the second
+ *     iterator comes before the first.
+ * @cmp_data: user data passed to @cmp_func
+ * 
+ * Like g_sequence_insert_sorted(), but uses
+ * a #GSequenceIterCompareFunc instead of a #GCompareDataFunc as
+ * the compare function.
+ * 
+ * Return value: a #GSequenceIter pointing to the new item
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_insert_sorted_iter (GSequence                *seq,
+                              gpointer                  data,
+                              GSequenceIterCompareFunc  iter_cmp,
+                              gpointer                  cmp_data)
+{
+  GSequenceNode *new_node;
+  GSequence *tmp_seq;
+
+  g_return_val_if_fail (seq != NULL, NULL);
+  g_return_val_if_fail (iter_cmp != NULL, NULL);
+  
+  check_seq_access (seq);
+
+  seq->access_prohibited = TRUE;
+  
+  /* Create a new temporary sequence and put the new node into
+   * that. The reason for this is that the user compare function
+   * will be called with the new node, and if it dereferences, 
+   * "is_end" will be called on it. But that will crash if the
+   * node is not actually in a sequence.
+   *
+   * node_insert_sorted() makes sure the node is unlinked before
+   * is is inserted.
+   *
+   * The reason we need the "iter" versions at all is that that
+   * is the only kind of compare functions GtkTreeView can use.
+   */
+  tmp_seq = g_sequence_new (NULL);
+  new_node = g_sequence_append (tmp_seq, data);
+  
+  node_insert_sorted (seq->end_node, new_node,
+                      seq->end_node, iter_cmp, cmp_data);
+  
+  g_sequence_free (tmp_seq);
+
+  seq->access_prohibited = FALSE;
+  
+  return new_node;
+}
+
+/**
+ * g_sequence_search_iter:
+ * @seq: a #GSequence
+ * @data: data for the new item
+ * @iter_cmp: the #GSequenceIterCompare function used to compare iterators
+ *     in the sequence. It is called with two iterators pointing into @seq.
+ *     It should return 0 if the iterators are equal, a negative value if the
+ *     first iterator comes before the second, and a positive value if the
+ *     second iterator comes before the first.
+ * @cmp_data: user data passed to @iter_cmp
+ *
+ * Like g_sequence_search(), but uses
+ * a #GSequenceIterCompareFunc instead of a #GCompareDataFunc as
+ * the compare function.
+ * 
+ * Return value: a #GSequenceIter pointing to the position in @seq
+ * where @data would have been inserted according to @iter_cmp and @cmp_data.
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_search_iter (GSequence                *seq,
+                        gpointer                  data,
+                        GSequenceIterCompareFunc  iter_cmp,
+                        gpointer                  cmp_data)
+{
+  GSequenceNode *node;
+  GSequenceNode *dummy;
+  GSequence *tmp_seq;
+  
+  g_return_val_if_fail (seq != NULL, NULL);
+  
+  check_seq_access (seq);
+  
+  seq->access_prohibited = TRUE;
+
+  /* Create a new temporary sequence and put the dummy node into
+   * that. The reason for this is that the user compare function
+   * will be called with the new node, and if it dereferences, 
+   * "is_end" will be called on it. But that will crash if the
+   * node is not actually in a sequence.
+   *
+   * node_insert_sorted() makes sure the node is unlinked before
+   * is is inserted.
+   *
+   * The reason we need the "iter" versions at all is that that
+   * is the only kind of compare functions GtkTreeView can use.
+   */
+  tmp_seq = g_sequence_new (NULL);
+  dummy = g_sequence_append (tmp_seq, data);
+  
+  node = node_find_closest (seq->end_node, dummy,
+                            seq->end_node, iter_cmp, cmp_data);
+
+  g_sequence_free (tmp_seq);
+  
+  seq->access_prohibited = FALSE;
+  
+  return node;
+}
+
+/**
+ * g_sequence_iter_get_sequence:
+ * @iter: a #GSequenceIter
+ * 
+ * Returns the #GSequence that @iter points into.
+ * 
+ * Return value: the #GSequence that @iter points into.
+ * 
+ * Since: 2.14
+ **/
+GSequence *
+g_sequence_iter_get_sequence (GSequenceIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  
+  return get_sequence (iter);
+}
+
+/**
+ * g_sequence_get:
+ * @iter: a #GSequenceIter
+ * 
+ * Returns the data that @iter points to.
+ * 
+ * Return value: the data that @iter points to
+ * 
+ * Since: 2.14
+ **/
+gpointer
+g_sequence_get (GSequenceIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (!is_end (iter), NULL);
+  
+  return iter->data;
+}
+
+/**
+ * g_sequence_set:
+ * @iter: a #GSequenceIter
+ * @data: new data for the item
+ * 
+ * Changes the data for the item pointed to by @iter to be @data. If
+ * the sequence has a data destroy function associated with it, that
+ * function is called on the existing data that @iter pointed to.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_set (GSequenceIter *iter,
+                gpointer       data)
+{
+  GSequence *seq;
+  
+  g_return_if_fail (iter != NULL);
+  g_return_if_fail (!is_end (iter));
+  
+  seq = get_sequence (iter);
+  
+  /* If @data is identical to iter->data, it is destroyed
+   * here. This will work right in case of ref-counted objects. Also
+   * it is similar to what ghashtables do.
+   *
+   * For non-refcounted data it's a little less convenient, but
+   * code relying on self-setting not destroying would be
+   * pretty dubious anyway ...
+   */
+  
+  if (seq->data_destroy_notify)
+    seq->data_destroy_notify (iter->data);
+  
+  iter->data = data;
+}
+
+/**
+ * g_sequence_get_length:
+ * @seq: a #GSequence
+ * 
+ * Returns the length of @seq
+ * 
+ * Return value: the length of @seq
+ * 
+ * Since: 2.14
+ **/
+gint
+g_sequence_get_length (GSequence *seq)
+{
+  return node_get_length (seq->end_node) - 1;
+}
+
+/**
+ * g_sequence_get_end_iter:
+ * @seq: a #GSequence 
+ * 
+ * Returns the end iterator for @seg
+ * 
+ * Return value: the end iterator for @seq
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_get_end_iter (GSequence *seq)
+{
+  g_return_val_if_fail (seq != NULL, NULL);
+  
+  g_assert (is_end (seq->end_node));
+  
+  return seq->end_node;
+}
+
+/**
+ * g_sequence_get_begin_iter:
+ * @seq: a #GSequence
+ * 
+ * Returns the begin iterator for @seq.
+ * 
+ * Return value: the begin iterator for @seq.
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_get_begin_iter (GSequence *seq)
+{
+  g_return_val_if_fail (seq != NULL, NULL);
+  return node_get_first (seq->end_node);
+}
+
+static int
+clamp_position (GSequence *seq,
+                int        pos)
+{
+  gint len = g_sequence_get_length (seq);
+  
+  if (pos > len || pos < 0)
+    pos = len;
+  
+  return pos;
+}
+
+/*
+ * if pos > number of items or -1, will return end pointer
+ */
+/**
+ * g_sequence_get_iter_at_pos:
+ * @seq: a #GSequence
+ * @pos: a position in @seq, or -1 for the end.
+ * 
+ * Returns the iterator at position @pos. If @pos is negative or larger
+ * than the number of items in @seq, the end iterator is returned.
+ * 
+ * Return value: The #GSequenceIter at position @pos
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_get_iter_at_pos (GSequence *seq,
+                            gint       pos)
+{
+  g_return_val_if_fail (seq != NULL, NULL);
+  
+  pos = clamp_position (seq, pos);
+  
+  return node_get_by_pos (seq->end_node, pos);
+}
+
+/**
+ * g_sequence_move:
+ * @src: a #GSequenceIter pointing to the item to move
+ * @dest: a #GSequenceIter pointing to the position to which
+ *        the item is moved.
+ *
+ * Moves the item pointed to by @src to the position indicated by @dest.
+ * After calling this function @dest will point to the position immediately
+ * after @src.
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_move (GSequenceIter *src,
+                 GSequenceIter *dest)
+{
+  g_return_if_fail (src != NULL);
+  g_return_if_fail (dest != NULL);
+  g_return_if_fail (!is_end (src));
+  
+  if (src == dest)
+    return;
+  
+  node_unlink (src);
+  node_insert_before (dest, src);
+}
+
+/* GSequenceIter */
+
+/**
+ * g_sequence_iter_is_end:
+ * @iter: a #GSequenceIter
+ * 
+ * Returns whether @iter is the end iterator
+ * 
+ * Return value: Whether @iter is the end iterator.
+ * 
+ * Since: 2.14
+ **/
+gboolean
+g_sequence_iter_is_end (GSequenceIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, FALSE);
+  
+  return is_end (iter);
+}
+
+/**
+ * g_sequence_iter_is_begin:
+ * @iter: a #GSequenceIter
+ * 
+ * Returns whether @iter is the begin iterator
+ * 
+ * Return value: whether @iter is the begin iterator
+ * 
+ * Since: 2.14
+ **/
+gboolean
+g_sequence_iter_is_begin (GSequenceIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, FALSE);
+  
+  return (node_get_prev (iter) == iter);
+}
+
+/**
+ * g_sequence_iter_get_position:
+ * @iter: a #GSequenceIter
+ * 
+ * Returns the position of @iter
+ * 
+ * Return value: the position of @iter
+ * 
+ * Since: 2.14
+ **/
+gint
+g_sequence_iter_get_position (GSequenceIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, -1);
+  
+  return node_get_pos (iter);
+}
+
+/**
+ * g_sequence_iter_next:
+ * @iter: a #GSequenceIter
+ * 
+ * Returns an iterator pointing to the next position after @iter. If
+ * @iter is the end iterator, the end iterator is returned.
+ * 
+ * Return value: a #GSequenceIter pointing to the next position after @iter.
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_iter_next (GSequenceIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  
+  return node_get_next (iter);
+}
+
+/**
+ * g_sequence_iter_prev:
+ * @iter: a #GSequenceIter
+ * 
+ * Returns an iterator pointing to the previous position before @iter. If
+ * @iter is the begin iterator, the begin iterator is returned.
+ * 
+ * Return value: a #GSequenceIter pointing to the previous position before
+ * @iter.
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_iter_prev (GSequenceIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  
+  return node_get_prev (iter);
+}
+
+/**
+ * g_sequence_iter_move:
+ * @iter: a #GSequenceIter
+ * @delta: A positive or negative number indicating how many positions away
+ *    from @iter the returned #GSequenceIter will be.
+ *
+ * Returns the #GSequenceIter which is @delta positions away from @iter.
+ * If @iter is closer than -@delta positions to the beginning of the sequence,
+ * the begin iterator is returned. If @iter is closer than @delta positions
+ * to the end of the sequence, the end iterator is returned.
+ *
+ * Return value: a #GSequenceIter which is @delta positions away from @iter.
+ * 
+ * Since: 2.14
+ **/
+GSequenceIter *
+g_sequence_iter_move (GSequenceIter *iter,
+                      gint           delta)
+{
+  gint new_pos;
+  
+  g_return_val_if_fail (iter != NULL, NULL);
+  
+  new_pos = node_get_pos (iter) + delta;
+  
+  new_pos = clamp_position (get_sequence (iter), new_pos);
+  
+  return node_get_by_pos (iter, new_pos);
+}
+
+/**
+ * g_sequence_swap:
+ * @a: a #GSequenceIter
+ * @b: a #GSequenceIter
+ * 
+ * Swaps the items pointed to by @a and @b
+ * 
+ * Since: 2.14
+ **/
+void
+g_sequence_swap (GSequenceIter *a,
+                 GSequenceIter *b)
+{
+  GSequenceNode *leftmost, *rightmost, *rightmost_next;
+  int a_pos, b_pos;
+  
+  g_return_if_fail (!g_sequence_iter_is_end (a));
+  g_return_if_fail (!g_sequence_iter_is_end (b));
+  
+  if (a == b)
+    return;
+  
+  a_pos = g_sequence_iter_get_position (a);
+  b_pos = g_sequence_iter_get_position (b);
+  
+  if (a_pos > b_pos)
+    {
+      leftmost = b;
+      rightmost = a;
+    }
+  else
+    {
+      leftmost = a;
+      rightmost = b;
+    }
+  
+  rightmost_next = node_get_next (rightmost);
+  
+  /* The situation is now like this:
+   *
+   *     ..., leftmost, ......., rightmost, rightmost_next, ...
+   *
+   */
+  g_sequence_move (rightmost, leftmost);
+  g_sequence_move (leftmost, rightmost_next);
+}
+
+/*
+ * Implementation of the splay tree. 
+ */
+static void
+node_update_fields (GSequenceNode *node)
+{
+  g_assert (node != NULL);
+  
+  node->n_nodes = 1;
+  
+  if (node->left)
+    node->n_nodes += node->left->n_nodes;
+  
+  if (node->right)
+    node->n_nodes += node->right->n_nodes;
+}
+
+#define NODE_LEFT_CHILD(n)  (((n)->parent) && ((n)->parent->left) == (n))
+#define NODE_RIGHT_CHILD(n) (((n)->parent) && ((n)->parent->right) == (n))
+
+static void
+node_rotate (GSequenceNode *node)
+{
+  GSequenceNode *tmp, *old;
+  
+  g_assert (node->parent);
+  g_assert (node->parent != node);
+  
+  if (NODE_LEFT_CHILD (node))
+    {
+      /* rotate right */
+      tmp = node->right;
+      
+      node->right = node->parent;
+      node->parent = node->parent->parent;
+      if (node->parent)
+        {
+          if (node->parent->left == node->right)
+            node->parent->left = node;
+          else
+            node->parent->right = node;
+        }
+      
+      g_assert (node->right);
+      
+      node->right->parent = node;
+      node->right->left = tmp;
+      
+      if (node->right->left)
+        node->right->left->parent = node->right;
+      
+      old = node->right;
+    }
+  else
+    {
+      /* rotate left */
+      tmp = node->left;
+      
+      node->left = node->parent;
+      node->parent = node->parent->parent;
+      if (node->parent)
+        {
+          if (node->parent->right == node->left)
+            node->parent->right = node;
+          else
+            node->parent->left = node;
+        }
+      
+      g_assert (node->left);
+      
+      node->left->parent = node;
+      node->left->right = tmp;
+      
+      if (node->left->right)
+        node->left->right->parent = node->left;
+      
+      old = node->left;
+    }
+  
+  node_update_fields (old);
+  node_update_fields (node);
+}
+
+static GSequenceNode *
+splay (GSequenceNode *node)
+{
+  while (node->parent)
+    {
+      if (!node->parent->parent)
+        {
+          /* zig */
+          node_rotate (node);
+        }
+      else if ((NODE_LEFT_CHILD (node) && NODE_LEFT_CHILD (node->parent)) ||
+               (NODE_RIGHT_CHILD (node) && NODE_RIGHT_CHILD (node->parent)))
+        {
+          /* zig-zig */
+          node_rotate (node->parent);
+          node_rotate (node);
+        }
+      else
+        {
+          /* zig-zag */
+          node_rotate (node);
+          node_rotate (node);
+        }
+    }
+  
+  return node;
+}
+
+static GSequenceNode *
+node_new (gpointer data)
+{
+  GSequenceNode *node = g_slice_new0 (GSequenceNode);
+  
+  node->parent = NULL;
+  node->parent = NULL;
+  node->left = NULL;
+  node->right = NULL;
+  
+  node->data = data;
+  node->n_nodes = 1;
+  
+  return node;
+}
+
+static GSequenceNode *
+find_min (GSequenceNode *node)
+{
+  splay (node);
+  
+  while (node->left)
+    node = node->left;
+  
+  return node;
+}
+
+static GSequenceNode *
+find_max (GSequenceNode *node)
+{
+  splay (node);
+  
+  while (node->right)
+    node = node->right;
+  
+  return node;
+}
+
+static GSequenceNode *
+node_get_first (GSequenceNode *node)
+{
+  return splay (find_min (node));
+}
+
+static GSequenceNode *
+node_get_last (GSequenceNode *node)
+{
+  return splay (find_max (node));
+}
+
+static gint
+get_n_nodes (GSequenceNode *node)
+{
+  if (node)
+    return node->n_nodes;
+  else
+    return 0;
+}
+
+static GSequenceNode *
+node_get_by_pos (GSequenceNode *node,
+                gint           pos)
+{
+  gint i;
+  
+  g_assert (node != NULL);
+  
+  splay (node);
+  
+  while ((i = get_n_nodes (node->left)) != pos)
+    {
+      if (i < pos)
+        {
+          node = node->right;
+          pos -= (i + 1);
+        }
+      else
+        {
+          node = node->left;
+          g_assert (node->parent != NULL);
+        }
+    }
+  
+  return splay (node);
+}
+
+static GSequenceNode *
+node_get_prev (GSequenceNode *node)
+{
+  splay (node);
+  
+  if (node->left)
+    {
+      node = node->left;
+      while (node->right)
+        node = node->right;
+    }
+  
+  return splay (node);
+}
+
+static GSequenceNode *
+node_get_next (GSequenceNode *node)
+{
+  splay (node);
+  
+  if (node->right)
+    {
+      node = node->right;
+      while (node->left)
+        node = node->left;
+   }
+  
+  return splay (node);
+}
+
+static gint
+node_get_pos (GSequenceNode *node)
+{
+  splay (node);
+  
+  return get_n_nodes (node->left);
+}
+
+/* Return closest node _strictly_ bigger than @needle. This node
+ * always exists because the tree has an explicit end node).
+ * This end node of @haystack must be passed in @end.
+ */
+static GSequenceNode *
+node_find_closest (GSequenceNode            *haystack,
+                   GSequenceNode            *needle,
+                   GSequenceNode            *end,
+                   GSequenceIterCompareFunc  iter_cmp,
+                   gpointer                  cmp_data)
+{
+  GSequenceNode *best;
+  gint c;
+  
+  g_assert (haystack);
+  
+  haystack = splay (haystack);
+  
+  do
+    {
+      best = haystack;
+      
+      /* iter_cmp can't be passed the end node, since the function may
+       * be user-supplied
+       */
+      if (haystack == end)
+        c = 1;
+      else
+        c = iter_cmp (haystack, needle, cmp_data);
+      
+      /* In the following we don't break even if c == 0. Instaed we go on
+       * searching along the 'bigger' nodes, so that we find the last one
+       * that is equal to the needle.
+       */
+      if (c > 0)
+        haystack = haystack->left;
+      else
+        haystack = haystack->right;
+    }
+  while (haystack != NULL);
+  
+  /* If the best node is smaller or equal to the data, then move one step
+   * to the right to make sure the best one is strictly bigger than the data
+   */
+  if (best != end && c <= 0)
+    best = node_get_next (best);
+  
+  return best;
+}
+
+static void
+node_free (GSequenceNode *node,
+           GSequence     *seq)
+{
+  GPtrArray *stack = g_ptr_array_new ();
+  
+  splay (node);
+
+  g_ptr_array_add (stack, node);
+  
+  while (stack->len > 0)
+    {
+      node = g_ptr_array_remove_index (stack, stack->len - 1);
+      
+      if (node)
+        {
+         g_ptr_array_add (stack, node->right);
+         g_ptr_array_add (stack, node->left);
+          
+          if (seq && seq->data_destroy_notify && node != seq->end_node)
+            seq->data_destroy_notify (node->data);
+          
+          g_slice_free (GSequenceNode, node);
+        }
+    }
+  
+  g_ptr_array_free (stack, TRUE);
+}
+
+/* Splits into two trees. @node will be part of the right tree
+ */
+static void
+node_cut (GSequenceNode *node)
+{
+  splay (node);
+  
+  g_assert (node->parent == NULL);
+  
+  if (node->left)
+    node->left->parent = NULL;
+  
+  node->left = NULL;
+  node_update_fields (node);
+}
+
+static void
+node_insert_before (GSequenceNode *node,
+                    GSequenceNode *new)
+{
+  g_assert (node != NULL);
+  g_assert (new != NULL);
+  
+  splay (node);
+  
+  new = splay (find_min (new));
+  g_assert (new->left == NULL);
+  
+  if (node->left)
+    node->left->parent = new;
+  
+  new->left = node->left;
+  new->parent = node;
+  
+  node->left = new;
+  
+  node_update_fields (new);
+  node_update_fields (node);
+}
+
+static void
+node_insert_after (GSequenceNode *node,
+                   GSequenceNode *new)
+{
+  g_assert (node != NULL);
+  g_assert (new != NULL);
+  
+  splay (node);
+  
+  new = splay (find_max (new));
+  g_assert (new->right == NULL);
+  g_assert (node->parent == NULL);
+  
+  if (node->right)
+    node->right->parent = new;
+  
+  new->right = node->right;
+  new->parent = node;
+  
+  node->right = new;
+  
+  node_update_fields (new);
+  node_update_fields (node);
+}
+
+static gint
+node_get_length (GSequenceNode    *node)
+{
+  g_assert (node != NULL);
+  
+  splay (node);
+  return node->n_nodes;
+}
+
+static void
+node_unlink (GSequenceNode *node)
+{
+  GSequenceNode *right, *left;
+  
+  splay (node);
+  
+  left = node->left;
+  right = node->right;
+  
+  node->parent = node->left = node->right = NULL;
+  node_update_fields (node);
+  
+  if (right)
+    {
+      right->parent = NULL;
+      
+      right = node_get_first (right);
+      g_assert (right->left == NULL);
+      
+      right->left = left;
+      if (left)
+        {
+          left->parent = right;
+          node_update_fields (right);
+        }
+    }
+  else if (left)
+    {
+      left->parent = NULL;
+    }
+}
+
+static void
+node_insert_sorted (GSequenceNode            *node,
+                    GSequenceNode            *new,
+                    GSequenceNode            *end,
+                    GSequenceIterCompareFunc  iter_cmp,
+                    gpointer                  cmp_data)
+{
+  GSequenceNode *closest;
+  
+  closest = node_find_closest (node, new, end, iter_cmp, cmp_data);
+  
+  node_unlink (new);
+  
+  node_insert_before (closest, new);
+}
+
+static gint
+node_calc_height (GSequenceNode *node)
+{
+  gint left_height;
+  gint right_height;
+  
+  if (node)
+    {
+      left_height = 0;
+      right_height = 0;
+      
+      if (node->left)
+        left_height = node_calc_height (node->left);
+      
+      if (node->right)
+        right_height = node_calc_height (node->right);
+      
+      return MAX (left_height, right_height) + 1;
+    }
+  
+  return 0;
+}
+
+/* Self-test function */
+static void
+check_node (GSequenceNode *node)
+{
+  if (node)
+    {
+      g_assert (node->parent != node);
+      g_assert (node->n_nodes ==
+                1 + get_n_nodes (node->left) + get_n_nodes (node->right));
+      check_node (node->left);
+      check_node (node->right);
+    }
+}
+
+void
+g_sequence_self_test_internal_to_glib_dont_use (GSequence *seq)
+{
+  GSequenceNode *node = splay (seq->end_node);
+  
+  check_node (node);
+}
+
+#define __G_SEQUENCE_C__
+#include "galiasdef.c"
diff --git a/glib/gsequence.h b/glib/gsequence.h
new file mode 100644 (file)
index 0000000..d9bdd6d
--- /dev/null
@@ -0,0 +1,121 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
+ * Soeren Sandmann (sandmann@daimi.au.dk)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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>
+
+#ifndef __G_SEQUENCE_H__
+#define __G_SEQUENCE_H__
+
+typedef struct _GSequence      GSequence;
+typedef struct _GSequenceNode  GSequenceIter;
+
+typedef gint (* GSequenceIterCompareFunc) (GSequenceIter *a,
+                                           GSequenceIter *b,
+                                           gpointer       data);
+
+
+/* GSequence */
+GSequence *    g_sequence_new                (GDestroyNotify            data_destroy);
+void           g_sequence_free               (GSequence                *seq);
+gint           g_sequence_get_length         (GSequence                *seq);
+void           g_sequence_foreach            (GSequence                *seq,
+                                              GFunc                     func,
+                                              gpointer                  user_data);
+void           g_sequence_foreach_range      (GSequenceIter            *begin,
+                                              GSequenceIter            *end,
+                                              GFunc                     func,
+                                              gpointer                  user_data);
+void           g_sequence_sort               (GSequence                *seq,
+                                              GCompareDataFunc          cmp_func,
+                                              gpointer                  cmp_data);
+void           g_sequence_sort_iter          (GSequence                *seq,
+                                              GSequenceIterCompareFunc  cmp_func,
+                                              gpointer                  cmp_data);
+
+
+/* Getting iters */
+GSequenceIter *g_sequence_get_begin_iter     (GSequence                *seq);
+GSequenceIter *g_sequence_get_end_iter       (GSequence                *seq);
+GSequenceIter *g_sequence_get_iter_at_pos    (GSequence                *seq,
+                                              gint                      pos);
+GSequenceIter *g_sequence_append             (GSequence                *seq,
+                                              gpointer                  data);
+GSequenceIter *g_sequence_prepend            (GSequence                *seq,
+                                              gpointer                  data);
+GSequenceIter *g_sequence_insert_before      (GSequenceIter            *iter,
+                                              gpointer                  data);
+void           g_sequence_move               (GSequenceIter            *src,
+                                              GSequenceIter            *dest);
+void           g_sequence_swap               (GSequenceIter            *a,
+                                              GSequenceIter            *b);
+GSequenceIter *g_sequence_insert_sorted      (GSequence                *seq,
+                                              gpointer                  data,
+                                              GCompareDataFunc          cmp_func,
+                                              gpointer                  cmp_data);
+GSequenceIter *g_sequence_insert_sorted_iter (GSequence                *seq,
+                                              gpointer                  data,
+                                              GSequenceIterCompareFunc  iter_cmp,
+                                              gpointer                  cmp_data);
+void           g_sequence_sort_changed       (GSequenceIter            *iter,
+                                              GCompareDataFunc          cmp_func,
+                                              gpointer                  cmp_data);
+void           g_sequence_sort_changed_iter  (GSequenceIter            *iter,
+                                              GSequenceIterCompareFunc  iter_cmp,
+                                              gpointer                  cmp_data);
+void           g_sequence_remove             (GSequenceIter            *iter);
+void           g_sequence_remove_range       (GSequenceIter            *begin,
+                                              GSequenceIter            *end);
+void           g_sequence_move_range         (GSequenceIter            *dest,
+                                              GSequenceIter            *begin,
+                                              GSequenceIter            *end);
+GSequenceIter *g_sequence_search             (GSequence                *seq,
+                                              gpointer                  data,
+                                              GCompareDataFunc          cmp_func,
+                                              gpointer                  cmp_data);
+GSequenceIter *g_sequence_search_iter        (GSequence                *seq,
+                                              gpointer                  data,
+                                              GSequenceIterCompareFunc  iter_cmp,
+                                              gpointer                  cmp_data);
+
+
+/* Dereferencing */
+gpointer       g_sequence_get                (GSequenceIter            *iter);
+void           g_sequence_set                (GSequenceIter            *iter,
+                                              gpointer                  data);
+
+/* Operations on GSequenceIter * */
+gboolean       g_sequence_iter_is_begin      (GSequenceIter            *iter);
+gboolean       g_sequence_iter_is_end        (GSequenceIter            *iter);
+GSequenceIter *g_sequence_iter_next          (GSequenceIter            *iter);
+GSequenceIter *g_sequence_iter_prev          (GSequenceIter            *iter);
+gint           g_sequence_iter_get_position  (GSequenceIter            *iter);
+GSequenceIter *g_sequence_iter_move          (GSequenceIter            *iter,
+                                              gint                      delta);
+GSequence *    g_sequence_iter_get_sequence  (GSequenceIter            *iter);
+
+
+/* Search */
+gint           g_sequence_iter_compare       (GSequenceIter            *a,
+                                              GSequenceIter            *b);
+GSequenceIter *g_sequence_range_get_midpoint (GSequenceIter            *begin,
+                                              GSequenceIter            *end);
+
+
+#endif /* __G_SEQUENCE_H__ */
index ec438d4..df5dd7b 100644 (file)
@@ -1,6 +1,6 @@
 SUBDIRS=gobject refcount
 
-INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/glib -I$(top_srcdir)/gmodule $(GLIB_DEBUG_FLAGS)
+INCLUDES = -g -I$(top_srcdir) -I$(top_srcdir)/glib -I$(top_srcdir)/gmodule $(GLIB_DEBUG_FLAGS)
 
 EFENCE=
 
@@ -93,6 +93,7 @@ test_programs =                                       \
        qsort-test                              \
        rand-test                               \
        relation-test                           \
+       sequence-test                           \
        shell-test                              \
        slist-test                              \
        slice-test                              \
@@ -160,6 +161,7 @@ asyncqueue_test_LDADD = $(thread_ldadd)
 qsort_test_LDADD = $(progs_ldadd)
 rand_test_LDADD = $(progs_ldadd)
 relation_test_LDADD = $(progs_ldadd)
+sequence_test_LDADD = $(progs_ldadd)
 shell_test_LDADD = $(progs_ldadd)
 slist_test_LDADD = $(progs_ldadd)
 slice_test_SOURCES = slice-test.c memchunks.c
diff --git a/tests/sequence-test.c b/tests/sequence-test.c
new file mode 100644 (file)
index 0000000..026dddf
--- /dev/null
@@ -0,0 +1,1153 @@
+#include <stdio.h>
+#include <glib.h>
+#include <stdlib.h>
+
+enum
+  {
+    NEW, FREE, GET_LENGTH, FOREACH, FOREACH_RANGE, SORT, SORT_ITER,
+    
+    /* Getting iters */
+    GET_BEGIN_ITER, GET_END_ITER, GET_ITER_AT_POS, APPEND, PREPEND,
+    INSERT_BEFORE, MOVE, INSERT_SORTED, INSERT_SORTED_ITER, SORT_CHANGED,
+    SORT_CHANGED_ITER, REMOVE, REMOVE_RANGE, MOVE_RANGE, SEARCH, SEARCH_ITER,
+    
+    /* dereferencing */
+    GET, SET,
+    
+    /* operations on GSequenceIter * */
+    ITER_IS_BEGIN, ITER_IS_END, ITER_NEXT, ITER_PREV, ITER_GET_POSITION,
+    ITER_MOVE, ITER_GET_SEQUENCE,
+    
+    /* search */
+    ITER_COMPARE, RANGE_GET_MIDPOINT,
+    N_OPS
+  } Op;
+
+typedef struct SequenceInfo
+{
+  GQueue *     queue;
+  GSequence *  sequence;
+  int          n_items;
+} SequenceInfo;
+
+void g_sequence_self_test_internal_to_glib_dont_use (GSequence *sequence);
+
+static void
+check_integrity (SequenceInfo *info)
+{
+  GList *list;
+  GSequenceIter *iter;
+  
+  g_sequence_self_test_internal_to_glib_dont_use (info->sequence);
+  
+  if (g_sequence_get_length (info->sequence) != info->n_items)
+    g_print ("%d %d\n",
+            g_sequence_get_length (info->sequence), info->n_items);
+  g_assert (info->n_items == g_queue_get_length (info->queue));
+  g_assert (g_sequence_get_length (info->sequence) == info->n_items);
+  
+  iter = g_sequence_get_begin_iter (info->sequence);
+  list = info->queue->head;
+  while (iter != g_sequence_get_end_iter (info->sequence))
+    {
+      g_assert (list->data == iter);
+      
+      iter = g_sequence_iter_next (iter);
+      list = list->next;
+    }
+  
+  g_assert (info->n_items == g_queue_get_length (info->queue));
+  g_assert (g_sequence_get_length (info->sequence) == info->n_items);
+}
+
+typedef struct
+{
+  SequenceInfo *seq;
+  int            number;
+} Item;
+
+static gpointer
+new_item (SequenceInfo *seq)
+{
+  Item *item = g_new (Item, 1);
+  seq->n_items++;
+  item->seq = seq;
+  item->number = g_random_int ();
+  
+  /* There have been bugs in the past where the GSequence would
+   * dereference the user pointers. This will make sure such
+   * behavior causes crashes
+   */
+  return ((char *)item + 1);
+}
+
+static Item *
+fix_pointer (gconstpointer data)
+{
+  return (Item *)((char *)data - 1);
+}
+
+static Item *
+get_item (GSequenceIter *iter)
+{
+  return fix_pointer (g_sequence_get (iter));
+}
+
+static void
+free_item (gpointer data)
+{
+  Item *item = fix_pointer (data);
+  item->seq->n_items--;
+  g_free (item);
+}
+
+static void
+seq_foreach (gpointer data,
+            gpointer user_data)
+{
+  Item *item = fix_pointer (data);
+  GList **link = user_data;
+  GSequenceIter *iter;
+  
+  g_assert (*link != NULL);
+  
+  iter = (*link)->data;
+  
+  g_assert (get_item (iter) == item);
+  
+  item->number = g_random_int();
+  
+  *link = (*link)->next;
+}
+
+static gint
+compare_items (gconstpointer a,
+              gconstpointer b,
+              gpointer      data)
+{
+  const Item *item_a = fix_pointer (a);
+  const Item *item_b = fix_pointer (b);
+  
+  if (item_a->number < item_b->number)
+    return -1;
+  else if (item_a->number == item_b->number)
+    return 0;
+  else
+    return 1;
+}
+
+static void
+check_sorted (SequenceInfo *info)
+{
+  GList *list;
+  int last;
+  GSequenceIter *last_iter;
+  
+  check_integrity (info);
+  
+  last = G_MININT;
+  last_iter = NULL;
+  for (list = info->queue->head; list != NULL; list = list->next)
+    {
+      GSequenceIter *iter = list->data;
+      Item *item = get_item (iter);
+      
+      g_assert (item->number >= last);
+      /* Check that the ordering is the same as that of the queue,
+       * ie. that the sort is stable
+       */
+      if (last_iter)
+       g_assert (iter == g_sequence_iter_next (last_iter));
+      
+      last = item->number;
+      last_iter = iter;
+    }
+}
+
+static gint
+compare_iters (gconstpointer a,
+              gconstpointer b,
+              gpointer      data)
+{
+  GSequenceIter *iter_a = (GSequenceIter *)a;
+  GSequenceIter *iter_b = (GSequenceIter *)b;
+  /* compare_items() will fix up the pointers */
+  Item *item_a = g_sequence_get (iter_a);
+  Item *item_b = g_sequence_get (iter_b);
+  
+  return compare_items (item_a, item_b, data);
+}
+
+/* A version of g_queue_link_index() that treats NULL as just
+ * beyond the queue
+ */
+static int
+queue_link_index (SequenceInfo *seq, GList *link)
+{
+  if (link)
+    return g_queue_link_index (seq->queue, link);
+  else
+    return g_queue_get_length (seq->queue);
+}
+
+static void
+get_random_range (SequenceInfo *seq,
+                 GSequenceIter **begin_iter,
+                 GSequenceIter **end_iter,
+                 GList **begin_link,
+                 GList **end_link)
+{
+  int length = g_queue_get_length (seq->queue);
+  int b = g_random_int_range (0, length + 1);
+  int e = g_random_int_range (b, length + 1);
+  
+  g_assert (length == g_sequence_get_length (seq->sequence));
+  
+  if (begin_iter)
+    *begin_iter = g_sequence_get_iter_at_pos (seq->sequence, b);
+  if (end_iter)
+    *end_iter = g_sequence_get_iter_at_pos (seq->sequence, e);
+  if (begin_link)
+    *begin_link = g_queue_peek_nth_link (seq->queue, b);
+  if (end_link)
+    *end_link = g_queue_peek_nth_link (seq->queue, e);
+  if (begin_iter && begin_link)
+    {
+      g_assert (
+               queue_link_index (seq, *begin_link) ==
+               g_sequence_iter_get_position (*begin_iter));
+    }
+  if (end_iter && end_link)
+    {
+      g_assert (
+               queue_link_index (seq, *end_link) ==
+               g_sequence_iter_get_position (*end_iter));
+    }
+}
+
+static gint
+get_random_position (SequenceInfo *seq)
+{
+  int length = g_queue_get_length (seq->queue);
+  
+  g_assert (length == g_sequence_get_length (seq->sequence));
+  
+  return g_random_int_range (-2, length + 5);
+}
+
+static GSequenceIter *
+get_random_iter (SequenceInfo  *seq,
+                GList        **link)
+{
+  GSequenceIter *iter;
+  int pos = get_random_position (seq);
+  if (link)
+    *link = g_queue_peek_nth_link (seq->queue, pos);
+  iter = g_sequence_get_iter_at_pos (seq->sequence, pos);
+  if (link)
+    g_assert (queue_link_index (seq, *link) == g_sequence_iter_get_position (iter));
+  return iter;
+}
+
+static void
+dump_info (SequenceInfo *seq)
+{
+#if 0
+  GSequenceIter *iter;
+  GList *list;
+  
+  iter = g_sequence_get_begin_iter (seq->sequence);
+  list = seq->queue->head;
+  
+  while (iter != g_sequence_get_end_iter (seq->sequence))
+    {
+      Item *item = get_item (iter);
+      g_print ("%p  %p    %d\n", list->data, iter, item->number);
+      
+      iter = g_sequence_iter_next (iter);
+      list = list->next;
+    }
+#endif
+}
+
+/* A version of g_queue_insert_before() that appends if link is NULL */
+static void
+queue_insert_before (SequenceInfo *seq, GList *link, gpointer data)
+{
+  if (link)
+    g_queue_insert_before (seq->queue, link, data);
+  else
+    g_queue_push_tail (seq->queue, data);
+}
+
+static void
+run_random_tests (guint32 seed)
+{
+#define N_ITERATIONS 60000
+#define N_SEQUENCES 8
+  
+  SequenceInfo sequences[N_SEQUENCES];
+  int k;
+  
+  g_print ("    seed: %u\n", seed);
+  
+  g_random_set_seed (seed);
+  
+  for (k = 0; k < N_SEQUENCES; ++k)
+    {
+      sequences[k].queue = g_queue_new ();
+      sequences[k].sequence = g_sequence_new (free_item);
+      sequences[k].n_items = 0;
+    }
+  
+#define RANDOM_SEQUENCE() &(sequences[g_random_int_range (0, N_SEQUENCES)])
+  
+  for (k = 0; k < N_ITERATIONS; ++k)
+    {
+      int i;
+      SequenceInfo *seq = RANDOM_SEQUENCE();
+      int op = g_random_int_range (0, N_OPS);
+      
+#if 0
+      g_print ("%d\n", op);
+#endif
+      
+      switch (op)
+       {
+       case NEW:
+       case FREE:
+         {
+           g_queue_free (seq->queue);
+           g_sequence_free (seq->sequence);
+           
+           g_assert (seq->n_items == 0);
+           
+           seq->queue = g_queue_new ();
+           seq->sequence = g_sequence_new (free_item);
+           
+           check_integrity (seq);
+         }
+         break;
+       case GET_LENGTH:
+         {
+           int slen = g_sequence_get_length (seq->sequence);
+           int qlen = g_queue_get_length (seq->queue);
+           
+           g_assert (slen == qlen);
+         }
+         break;
+       case FOREACH:
+         {
+           GList *link = seq->queue->head;
+           g_sequence_foreach (seq->sequence, seq_foreach, &link);
+           g_assert (link == NULL);
+         }
+         break;
+       case FOREACH_RANGE:
+         {
+           GSequenceIter *begin_iter, *end_iter;
+           GList *begin_link, *end_link;
+           
+           get_random_range (seq, &begin_iter, &end_iter, &begin_link, &end_link);
+           
+           check_integrity (seq);
+           
+           g_sequence_foreach_range (begin_iter, end_iter, seq_foreach, &begin_link);
+           
+           g_assert (begin_link == end_link);
+         }
+         break;
+       case SORT:
+         {
+           dump_info (seq);
+           
+           g_sequence_sort (seq->sequence, compare_items, NULL);
+           g_queue_sort (seq->queue, compare_iters, NULL);
+           
+           check_sorted (seq);
+           
+           dump_info (seq);
+         }
+         break;
+       case SORT_ITER:
+         {
+           g_sequence_sort_iter (seq->sequence,
+                                 (GSequenceIterCompareFunc)compare_iters, NULL);
+           g_queue_sort (seq->queue, compare_iters, NULL);
+           check_sorted (seq);
+         }
+         break;
+         
+         /* Getting iters */
+       case GET_END_ITER:
+       case GET_BEGIN_ITER:
+         {
+           GSequenceIter *begin_iter;
+           GSequenceIter *end_iter;
+           GSequenceIter *penultimate_iter;
+           
+           begin_iter = g_sequence_get_begin_iter (seq->sequence);
+           check_integrity (seq);
+           
+           end_iter = g_sequence_get_end_iter (seq->sequence);
+           check_integrity (seq);
+           
+           penultimate_iter = g_sequence_iter_prev (end_iter);
+           check_integrity (seq);
+           
+           if (g_sequence_get_length (seq->sequence) > 0)
+             {
+               g_assert (seq->queue->head);
+               g_assert (seq->queue->head->data == begin_iter);
+               g_assert (seq->queue->tail);
+               g_assert (seq->queue->tail->data == penultimate_iter);
+             }
+           else
+             {
+               g_assert (penultimate_iter == end_iter);
+               g_assert (begin_iter == end_iter);
+               g_assert (penultimate_iter == begin_iter);
+               g_assert (seq->queue->head == NULL);
+               g_assert (seq->queue->tail == NULL);
+             }
+         }
+         break;
+       case GET_ITER_AT_POS:
+         {
+           int i;
+           
+           g_assert (g_queue_get_length (seq->queue) == g_sequence_get_length (seq->sequence));
+           
+           for (i = 0; i < 10; ++i)
+             {
+               int pos = get_random_position (seq);
+               GSequenceIter *iter = g_sequence_get_iter_at_pos (seq->sequence, pos);
+               GList *link = g_queue_peek_nth_link (seq->queue, pos);
+               check_integrity (seq);
+               if (pos >= g_sequence_get_length (seq->sequence) || pos < 0)
+                 {
+                   g_assert (iter == g_sequence_get_end_iter (seq->sequence));
+                   g_assert (link == NULL);
+                 }
+               else
+                 {
+                   g_assert (link);
+                   g_assert (link->data == iter);
+                 }
+             }
+         }
+         break;
+       case APPEND:
+         {
+           for (i = 0; i < 10; ++i)
+             {
+               GSequenceIter *iter = g_sequence_append (seq->sequence, new_item (seq));
+               g_queue_push_tail (seq->queue, iter);
+             }
+         }
+         break;
+       case PREPEND:
+         {
+           for (i = 0; i < 10; ++i)
+             {
+               GSequenceIter *iter = g_sequence_prepend (seq->sequence, new_item (seq));
+               g_queue_push_head (seq->queue, iter);
+             }
+         }
+         break;
+       case INSERT_BEFORE:
+         {
+           for (i = 0; i < 10; ++i)
+             {
+               GList *link;
+               GSequenceIter *iter = get_random_iter (seq, &link);
+               GSequenceIter *new_iter;
+               check_integrity (seq);
+               
+               new_iter = g_sequence_insert_before (iter, new_item (seq));
+               
+               queue_insert_before (seq, link, new_iter);
+             }
+         }
+         break;
+       case MOVE:
+         {
+           GList *link1, *link2;
+           GSequenceIter *iter1 = get_random_iter (seq, &link1);
+           GSequenceIter *iter2 = get_random_iter (seq, &link2);
+           
+           if (!g_sequence_iter_is_end (iter1))
+             {
+               g_sequence_move (iter1, iter2);
+               
+               if (!link2)
+                 g_assert (g_sequence_iter_is_end (iter2));
+               
+               queue_insert_before (seq, link2, link1->data);
+               
+               g_queue_delete_link (seq->queue, link1);
+             }
+           
+           check_integrity (seq);
+           
+           iter1 = get_random_iter (seq, NULL);
+           
+           /* Moving an iter to itself should have no effect */
+           if (!g_sequence_iter_is_end (iter1))
+             g_sequence_move (iter1, iter1);
+         }
+         break;
+       case INSERT_SORTED:
+         {
+           int i;
+           dump_info (seq);
+           
+           g_sequence_sort (seq->sequence, compare_items, NULL);
+           g_queue_sort (seq->queue, compare_iters, NULL);
+           
+           check_sorted (seq);
+           
+           for (i = 0; i < 15; ++i)
+             {
+               GSequenceIter *iter =
+                 g_sequence_insert_sorted (
+                                           seq->sequence, new_item(seq), compare_items, NULL);
+               
+               g_queue_insert_sorted (seq->queue, iter, compare_iters, NULL);
+             }
+           
+           check_sorted (seq);
+           
+           dump_info (seq);
+         }
+         break;
+       case INSERT_SORTED_ITER:
+         {
+           int i;
+           dump_info (seq);
+           
+           g_sequence_sort (seq->sequence, compare_items, NULL);
+           g_queue_sort (seq->queue, compare_iters, NULL);
+           
+           check_sorted (seq);
+           
+           for (i = 0; i < 15; ++i)
+             {
+               GSequenceIter *iter;
+
+               iter = g_sequence_insert_sorted_iter (seq->sequence,
+                                                     new_item (seq),
+                                                     (GSequenceIterCompareFunc)compare_iters,
+                                                     NULL);
+               
+               g_queue_insert_sorted (seq->queue, iter, compare_iters, NULL);
+             }
+           
+           check_sorted (seq);
+           
+           dump_info (seq);
+         }
+         break;
+       case SORT_CHANGED:
+         {
+           int i;
+           
+           g_sequence_sort (seq->sequence, compare_items, NULL);
+           g_queue_sort (seq->queue, compare_iters, NULL);
+           
+           check_sorted (seq);
+           
+           for (i = 0; i < 15; ++i)
+             {
+               GList *link;
+               GSequenceIter *iter = get_random_iter (seq, &link);
+               
+               if (!g_sequence_iter_is_end (iter))
+                 {
+                   g_sequence_set (iter, new_item (seq));
+                   g_sequence_sort_changed (iter, compare_items, NULL);
+                   
+                   g_queue_delete_link (seq->queue, link);
+                   g_queue_insert_sorted (seq->queue, iter, compare_iters, NULL);
+                 }
+               
+               check_sorted (seq);
+             }
+         }
+         break;
+       case SORT_CHANGED_ITER:
+         {
+           int i;
+           
+           g_sequence_sort (seq->sequence, compare_items, NULL);
+           g_queue_sort (seq->queue, compare_iters, NULL);
+           
+           check_sorted (seq);
+           
+           for (i = 0; i < 15; ++i)
+             {
+               GList *link;
+               GSequenceIter *iter = get_random_iter (seq, &link);
+               
+               if (!g_sequence_iter_is_end (iter))
+                 {
+                   g_sequence_set (iter, new_item (seq));
+                   g_sequence_sort_changed_iter (iter,
+                                                 (GSequenceIterCompareFunc)compare_iters, NULL);
+                   
+                   g_queue_delete_link (seq->queue, link);
+                   g_queue_insert_sorted (seq->queue, iter, compare_iters, NULL);
+                 }
+               
+               check_sorted (seq);
+             }
+         }
+         break;
+       case REMOVE:
+         {
+           int i;
+           
+           for (i = 0; i < 15; ++i)
+             {
+               GList *link;
+               GSequenceIter *iter = get_random_iter (seq, &link);
+               
+               if (!g_sequence_iter_is_end (iter))
+                 {
+                   g_sequence_remove (iter);
+                   g_queue_delete_link (seq->queue, link);
+                 }
+             }
+         }
+         break;
+       case REMOVE_RANGE:
+         {
+           GSequenceIter *begin_iter, *end_iter;
+           GList *begin_link, *end_link;
+           GList *list;
+           
+           get_random_range (seq, &begin_iter, &end_iter, &begin_link, &end_link);
+           
+           g_sequence_remove_range (begin_iter, end_iter);
+           
+           list = begin_link;
+           while (list != end_link)
+             {
+               GList *next = list->next;
+               
+               g_queue_delete_link (seq->queue, list);
+               
+               list = next;
+             }
+         }
+         break;
+       case MOVE_RANGE:
+         {
+           SequenceInfo *src = RANDOM_SEQUENCE();
+           SequenceInfo *dst = RANDOM_SEQUENCE();
+           
+           GSequenceIter *begin_iter, *end_iter;
+           GList *begin_link, *end_link;
+           
+           GSequenceIter *dst_iter;
+           GList *dst_link;
+           
+           GList *list;
+           
+           g_assert (src->queue);
+           g_assert (dst->queue);
+           
+           get_random_range (src, &begin_iter, &end_iter, &begin_link, &end_link);
+           dst_iter = get_random_iter (dst, &dst_link);
+           
+           g_sequence_move_range (dst_iter, begin_iter, end_iter);
+           
+           if (dst_link == begin_link || (src == dst && dst_link == end_link))
+             {
+               check_integrity (src);
+               check_integrity (dst);
+               break;
+             }
+           
+           if (queue_link_index (src, begin_link) >=
+               queue_link_index (src, end_link))
+             {
+               break;
+             }
+           
+           if (src == dst &&
+               queue_link_index (src, dst_link) >= queue_link_index (src, begin_link) &&
+               queue_link_index (src, dst_link) <= queue_link_index (src, end_link))
+             {
+               break;
+             }
+           
+           list = begin_link;
+           while (list != end_link)
+             {
+               GList *next = list->next;
+               Item *item = get_item (list->data);
+               
+               g_assert (dst->queue);
+               queue_insert_before (dst, dst_link, list->data);
+               g_queue_delete_link (src->queue, list);
+               
+               g_assert (item->seq == src);
+               
+               src->n_items--;
+               dst->n_items++;
+               item->seq = dst;
+               
+               list = next;
+             }
+         }
+         break;
+       case SEARCH:
+         {
+           Item *item;
+           GSequenceIter *search_iter;
+           GSequenceIter *insert_iter;
+           
+           g_sequence_sort (seq->sequence, compare_items, NULL);
+           g_queue_sort (seq->queue, compare_iters, NULL);
+           
+           check_sorted (seq);
+           
+           item = new_item (seq);
+           search_iter = g_sequence_search (seq->sequence, item, compare_items, NULL);
+           
+           insert_iter = g_sequence_insert_sorted (seq->sequence, item, compare_items, NULL);
+           
+           g_assert (search_iter == g_sequence_iter_next (insert_iter));
+           
+           g_queue_insert_sorted (seq->queue, insert_iter, compare_iters, NULL);
+         }
+         break;
+       case SEARCH_ITER:
+         {
+           Item *item;
+           GSequenceIter *search_iter;
+           GSequenceIter *insert_iter;
+           
+           g_sequence_sort (seq->sequence, compare_items, NULL);
+           g_queue_sort (seq->queue, compare_iters, NULL);
+           
+           check_sorted (seq);
+           
+           item = new_item (seq);
+           search_iter = g_sequence_search_iter (seq->sequence,
+                                                 item,
+                                                 (GSequenceIterCompareFunc)compare_iters, NULL);
+           
+           insert_iter = g_sequence_insert_sorted (seq->sequence, item, compare_items, NULL);
+           
+           g_assert (search_iter == g_sequence_iter_next (insert_iter));
+           
+           g_queue_insert_sorted (seq->queue, insert_iter, compare_iters, NULL);
+         }
+         break;
+         
+         /* dereferencing */
+       case GET:
+       case SET:
+         {
+           GSequenceIter *iter;
+           GList *link;
+           
+           iter = get_random_iter (seq, &link);
+           
+           if (!g_sequence_iter_is_end (iter))
+             {
+               Item *item;
+               int i;
+               
+               check_integrity (seq);
+               
+               /* Test basic functionality */
+               item = new_item (seq);
+               g_sequence_set (iter, item);
+               g_assert (g_sequence_get (iter) == item);
+               
+               /* Make sure that existing items are freed */
+               for (i = 0; i < 15; ++i)
+                 g_sequence_set (iter, new_item (seq));
+               
+               check_integrity (seq);
+               
+               g_sequence_set (iter, new_item (seq));
+             }
+         }
+         break;
+         
+         /* operations on GSequenceIter * */
+       case ITER_IS_BEGIN:
+         {
+           GSequenceIter *iter;
+           
+           iter = g_sequence_get_iter_at_pos (seq->sequence, 0);
+           
+           g_assert (g_sequence_iter_is_begin (iter));
+           
+           check_integrity (seq);
+           
+           if (g_sequence_get_length (seq->sequence) > 0)
+             {
+               g_assert (!g_sequence_iter_is_begin (
+                                                    g_sequence_get_end_iter (seq->sequence)));
+             }
+           else
+             {
+               g_assert (g_sequence_iter_is_begin (
+                                                   g_sequence_get_end_iter (seq->sequence)));
+             }
+           
+           g_assert (g_sequence_iter_is_begin (g_sequence_get_begin_iter (seq->sequence)));
+         }
+         break;
+       case ITER_IS_END:
+         {
+           GSequenceIter *iter;
+           int len = g_sequence_get_length (seq->sequence);
+           
+           iter = g_sequence_get_iter_at_pos (seq->sequence, len);
+           
+           g_assert (g_sequence_iter_is_end (iter));
+           
+           if (len > 0)
+             {
+               g_assert (!g_sequence_iter_is_end (
+                                                  g_sequence_get_begin_iter (seq->sequence)));
+             }
+           else
+             {
+               g_assert (g_sequence_iter_is_end (
+                                                 g_sequence_get_begin_iter (seq->sequence)));
+             }
+           
+           g_assert (g_sequence_iter_is_end (g_sequence_get_end_iter (seq->sequence)));
+         }
+         break;
+       case ITER_NEXT:
+         {
+           GSequenceIter *iter1, *iter2, *iter3, *end;
+           
+           iter1 = g_sequence_append (seq->sequence, new_item (seq));
+           iter2 = g_sequence_append (seq->sequence, new_item (seq));
+           iter3 = g_sequence_append (seq->sequence, new_item (seq));
+           
+           end = g_sequence_get_end_iter (seq->sequence);
+           
+           g_assert (g_sequence_iter_next (iter1) == iter2);
+           g_assert (g_sequence_iter_next (iter2) == iter3);
+           g_assert (g_sequence_iter_next (iter3) == end);
+           g_assert (g_sequence_iter_next (end) == end);
+           
+           g_queue_push_tail (seq->queue, iter1);
+           g_queue_push_tail (seq->queue, iter2);
+           g_queue_push_tail (seq->queue, iter3);
+         }
+         break;
+       case ITER_PREV:
+         {
+           GSequenceIter *iter1, *iter2, *iter3, *begin;
+           
+           iter1 = g_sequence_prepend (seq->sequence, new_item (seq));
+           iter2 = g_sequence_prepend (seq->sequence, new_item (seq));
+           iter3 = g_sequence_prepend (seq->sequence, new_item (seq));
+           
+           begin = g_sequence_get_begin_iter (seq->sequence);
+           
+           g_assert (g_sequence_iter_prev (iter1) == iter2);
+           g_assert (g_sequence_iter_prev (iter2) == iter3);
+           g_assert (iter3 == begin);
+           g_assert (g_sequence_iter_prev (iter3) == begin);
+           g_assert (g_sequence_iter_prev (begin) == begin);
+           
+           g_queue_push_head (seq->queue, iter1);
+           g_queue_push_head (seq->queue, iter2);
+           g_queue_push_head (seq->queue, iter3);
+         }
+         break;
+       case ITER_GET_POSITION:
+         {
+           GList *link;
+           GSequenceIter *iter = get_random_iter (seq, &link);
+           
+           g_assert (g_sequence_iter_get_position (iter) ==
+                     queue_link_index (seq, link));
+         }
+         break;
+       case ITER_MOVE:
+         {
+           int len = g_sequence_get_length (seq->sequence);
+           GSequenceIter *iter;
+           int pos;
+           
+           iter = get_random_iter (seq, NULL);
+           pos = g_sequence_iter_get_position (iter);
+           iter = g_sequence_iter_move (iter, len - pos);
+           g_assert (g_sequence_iter_is_end (iter));
+           
+           
+           iter = get_random_iter (seq, NULL);
+           pos = g_sequence_iter_get_position (iter);
+           while (pos < len)
+             {
+               g_assert (!g_sequence_iter_is_end (iter));
+               pos++;
+               iter = g_sequence_iter_move (iter, 1);
+             }
+           g_assert (g_sequence_iter_is_end (iter));
+         }
+         break;
+       case ITER_GET_SEQUENCE:
+         {
+           GSequenceIter *iter = get_random_iter (seq, NULL);
+           
+           g_assert (g_sequence_iter_get_sequence (iter) == seq->sequence);
+         }
+         break;
+         
+         /* search */
+       case ITER_COMPARE:
+         {
+           GList *link1, *link2;
+           GSequenceIter *iter1 = get_random_iter (seq, &link1);
+           GSequenceIter *iter2 = get_random_iter (seq, &link2);
+           
+           int cmp = g_sequence_iter_compare (iter1, iter2);
+           int pos1 = queue_link_index (seq, link1);
+           int pos2 = queue_link_index (seq, link2);
+           
+           if (cmp == 0)
+             {
+               g_assert (pos1 == pos2);
+             }
+           else if (cmp < 0)
+             {
+               g_assert (pos1 < pos2);
+             }
+           else
+             {
+               g_assert (pos1 > pos2);
+             }
+         }
+         break;
+       case RANGE_GET_MIDPOINT:
+         {
+           GSequenceIter *iter1 = get_random_iter (seq, NULL);
+           GSequenceIter *iter2 = get_random_iter (seq, NULL);
+           GSequenceIter *iter3;
+           int cmp;
+           
+           cmp = g_sequence_iter_compare (iter1, iter2);
+           
+           if (cmp > 0)
+             {
+               GSequenceIter *tmp;
+               
+               tmp = iter1;
+               iter1 = iter2;
+               iter2 = tmp;
+             }
+           
+           iter3 = g_sequence_range_get_midpoint (iter1, iter2);
+           
+           if (cmp == 0)
+             {
+               g_assert (iter3 == iter1);
+               g_assert (iter3 == iter2);
+             }
+           
+           g_assert (g_sequence_iter_get_position (iter3) >= 
+                     g_sequence_iter_get_position (iter1));
+           g_assert (g_sequence_iter_get_position (iter2) >= 
+                     g_sequence_iter_get_position (iter3));
+         }
+         break;
+         
+       }
+      
+      check_integrity (seq);
+    }
+}
+
+/* Random seeds known to have failed at one point
+ */
+static gulong seeds[] =
+  {
+    801678400u,
+    1477639090u,
+    3369132895u,
+    1192944867u,
+    770458294u,
+    1099575817u,
+    590523467u,
+    3583571454u,
+    579241222u
+  };
+
+static void standalone_tests (void);
+
+static guint32
+get_seed (int argc, char **argv)
+{
+  if (argc > 1)
+    {
+      char *endptr;
+      
+      return strtol (argv[1], &endptr, 0);
+    }
+  else
+    {
+      return g_random_int();
+    }
+}
+
+int
+main (int argc,
+      char **argv)
+{
+  guint32 seed = get_seed (argc, argv);
+  int i;
+  
+  /* Run stand alone tests */
+  g_print ("running standalone tests\n");
+  standalone_tests();
+  
+  g_print ("running regression tests:\n");
+  /* Run regression tests */
+  for (i = 0; i < G_N_ELEMENTS (seeds); ++i)
+    {
+      run_random_tests (seeds[i]);
+    }
+  
+  /* Run with a new random seed */
+  g_print ("random seed:\n");
+  run_random_tests (seed);
+  
+  
+  return 0;
+}
+
+
+/* Single, stand-alone tests */
+
+static void
+test_out_of_range_jump (void)
+{
+  GSequence *seq = g_sequence_new (NULL);
+  GSequenceIter *iter = g_sequence_get_begin_iter (seq);
+  
+  g_sequence_iter_move (iter, 5);
+  
+  g_assert (g_sequence_iter_is_begin (iter));
+  g_assert (g_sequence_iter_is_end (iter));
+}
+
+static int
+compare (gconstpointer a, gconstpointer b, gpointer userdata)
+{
+  int ai, bi;
+  
+  ai = GPOINTER_TO_INT (a);
+  bi = GPOINTER_TO_INT (b);
+  
+  if (ai < bi)
+    return -1;
+  else if (ai > bi)
+    return 1;
+  else
+    return 0;
+}
+
+static int
+compare_iter (GSequenceIter *a,
+             GSequenceIter *b,
+             gpointer data)
+{
+  return compare (g_sequence_get (a),
+                 g_sequence_get (b),
+                 data);
+}
+
+static void
+test_insert_sorted_non_pointer (void)
+{
+  int i;
+  
+  for (i = 0; i < 10; i++)
+    {
+      GSequence *seq = g_sequence_new (NULL);
+      int j;
+      
+      for (j = 0; j < 10000; j++)
+       {
+         g_sequence_insert_sorted (
+                                   seq, GINT_TO_POINTER (g_random_int()),
+                                   compare, NULL);
+         
+         g_sequence_insert_sorted_iter (
+                                        seq, GINT_TO_POINTER (g_random_int()),
+                                        compare_iter, NULL);
+       }
+      
+      g_sequence_free (seq);
+    }
+}
+
+static void
+test_stable_sort (void)
+{
+  int i;
+  GSequence *seq = g_sequence_new (NULL);
+  
+#define N_ITEMS 1000
+  
+  GSequenceIter *iters[N_ITEMS];
+  GSequenceIter *iter;
+  
+  for (i = 0; i < N_ITEMS; ++i)
+    iters[i] = g_sequence_append (seq, GINT_TO_POINTER (3000));
+  
+  i = 0;
+  iter = g_sequence_get_begin_iter (seq);
+  while (!g_sequence_iter_is_end (iter))
+    {
+      g_assert (iters[i++] == iter);
+      
+      iter = g_sequence_iter_next (iter);
+    }
+  
+  g_sequence_sort (seq, compare, NULL);
+  
+  i = 0;
+  iter = g_sequence_get_begin_iter (seq);
+  while (!g_sequence_iter_is_end (iter))
+    {
+      g_assert (iters[i++] == iter);
+      
+      iter = g_sequence_iter_next (iter);
+    }
+  
+  for (i = N_ITEMS - 1; i >= 0; --i)
+    g_sequence_sort_changed (iters[i], compare, NULL);
+  
+  i = 0;
+  iter = g_sequence_get_begin_iter (seq);
+  while (!g_sequence_iter_is_end (iter))
+    {
+      g_assert (iters[i++] == iter);
+      
+      iter = g_sequence_iter_next (iter);
+    }
+}
+
+static void
+standalone_tests (void)
+{
+  test_out_of_range_jump ();
+  test_insert_sorted_non_pointer ();
+  test_stable_sort ();
+}
+