Marc Nieper-Wißkirchen wrote:
> For example, given a list of fruits, insert "pear" after each "apple" and
> "peach" before each "apple". This can be easily done through
> gl_list_add_before/gl_list_add_after/gl_list_next_node without using
> invalidated nodes.

Now I see what you mean. Quasi companion functions to gl_list_next_node
and gl_list_previous_node: gl_list_first_node and gl_list_last_node,
respectively.

> What I want to propose is to allow the NULL value in
> gl_next_node/gl_previous_node. In this case, gl_next_node shall return the
> first node and gl_previous_node shall return the last node.

I don't think this encourages robust programs. If a program passes a NULL
pointer to gl_list_next_node or gl_list_previous_node, the program should
better crash.

> PS What can't be done (because gl_list_remove doesn't return a valid
> (next?) node) is list walking algorithms that both remove and insert nodes.
> For removal, one has to use an iterator.

Yes, some algorithms need a second, temporary list. Not all algorithms
can be written to use the original list, be efficient in O() terms, and
be implementation-independent.


2021-04-03  Bruno Haible  <br...@clisp.org>

        *-list tests: Add more tests.
        * tests/test-array_list.c (check_equals_by_forward_iteration,
        check_equals_by_backward_iteration): New functions.
        (main): Invoke them.
        * tests/test-carray_list.c: Likewise.
        * tests/test-linked_list.c: Likewise.
        * tests/test-linkedhash_list.c: Likewise.
        * tests/test-avltree_list.c: Likewise.
        * tests/test-avltreehash_list.c: Likewise.
        * tests/test-rbtree_list.c: Likewise.
        * tests/test-rbtreehash_list.c: Likewise.

        list: Add operations first_node, last_node.
        Reported by Marc Nieper-Wißkirchen in
        <https://lists.gnu.org/archive/html/bug-gnulib/2021-04/msg00005.html>.
        * lib/gl_list.h (gl_list_first_node, gl_list_last_node): New functions.
        (struct gl_list_implementation): Add members first_node, last_node.
        * lib/gl_array_list.c (gl_array_first_node, gl_array_last_node): New
        functions.
        (gl_array_list_implementation): Add the new operations.
        * lib/gl_carray_list.c (gl_carray_first_node, gl_carray_last_node): New
        functions.
        (gl_carray_list_implementation): Add the new operations.
        * lib/gl_anylinked_list2.h (gl_linked_first_node, gl_linked_last_node):
        New functions.
        * lib/gl_linked_list.c (gl_linked_list_implementation): Add the new
        operations.
        * lib/gl_linkedhash_list.c (gl_linkedhash_list_implementation):
        Likewise.
        * lib/gl_anytree_list2.h (gl_tree_first_node, gl_tree_last_node): New
        functions.
        * lib/gl_avltree_list.c (gl_avltree_list_implementation): Add the new
        operations.
        * lib/gl_avltreehash_list.c (gl_avltreehash_list_implementation):
        Likewise.
        * lib/gl_rbtree_list.c (gl_rbtree_list_implementation): Likewise.
        * lib/gl_rbtreehash_list.c (gl_rbtreehash_list_implementation):
        Likewise.
        * lib/gl_sublist.c (gl_sublist_first_node, gl_sublist_last_node): New
        functions.
        (gl_sublist_list_implementation): Add the new operations.
        * lib/gl_list.hh (class gl_List): Add member functions first_node,
        last_node.
        * doc/containers.texi: Update table.

From cce713624d68c60d9d6eeb11747500db998821a2 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sat, 3 Apr 2021 17:59:47 +0200
Subject: [PATCH 1/2] list: Add operations first_node, last_node.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Reported by Marc Nieper-Wißkirchen in
<https://lists.gnu.org/archive/html/bug-gnulib/2021-04/msg00005.html>.

* lib/gl_list.h (gl_list_first_node, gl_list_last_node): New functions.
(struct gl_list_implementation): Add members first_node, last_node.
* lib/gl_array_list.c (gl_array_first_node, gl_array_last_node): New
functions.
(gl_array_list_implementation): Add the new operations.
* lib/gl_carray_list.c (gl_carray_first_node, gl_carray_last_node): New
functions.
(gl_carray_list_implementation): Add the new operations.
* lib/gl_anylinked_list2.h (gl_linked_first_node, gl_linked_last_node):
New functions.
* lib/gl_linked_list.c (gl_linked_list_implementation): Add the new
operations.
* lib/gl_linkedhash_list.c (gl_linkedhash_list_implementation):
Likewise.
* lib/gl_anytree_list2.h (gl_tree_first_node, gl_tree_last_node): New
functions.
* lib/gl_avltree_list.c (gl_avltree_list_implementation): Add the new
operations.
* lib/gl_avltreehash_list.c (gl_avltreehash_list_implementation):
Likewise.
* lib/gl_rbtree_list.c (gl_rbtree_list_implementation): Likewise.
* lib/gl_rbtreehash_list.c (gl_rbtreehash_list_implementation):
Likewise.
* lib/gl_sublist.c (gl_sublist_first_node, gl_sublist_last_node): New
functions.
(gl_sublist_list_implementation): Add the new operations.
* lib/gl_list.hh (class gl_List): Add member functions first_node,
last_node.
* doc/containers.texi: Update table.
---
 ChangeLog                 | 35 +++++++++++++++++++++++++++++++++++
 doc/containers.texi       | 18 ++++++++++++++++++
 lib/gl_anylinked_list2.h  | 18 ++++++++++++++++++
 lib/gl_anytree_list2.h    | 26 ++++++++++++++++++++++++++
 lib/gl_array_list.c       | 20 ++++++++++++++++++++
 lib/gl_avltree_list.c     |  2 ++
 lib/gl_avltreehash_list.c |  2 ++
 lib/gl_carray_list.c      | 20 ++++++++++++++++++++
 lib/gl_linked_list.c      |  2 ++
 lib/gl_linkedhash_list.c  |  2 ++
 lib/gl_list.h             | 35 +++++++++++++++++++++++++++++++++++
 lib/gl_list.hh            |  8 ++++++++
 lib/gl_rbtree_list.c      |  2 ++
 lib/gl_rbtreehash_list.c  |  2 ++
 lib/gl_sublist.c          | 21 +++++++++++++++++++++
 15 files changed, 213 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 9323aa9..958ad02 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,40 @@
 2021-04-03  Bruno Haible  <br...@clisp.org>
 
+	list: Add operations first_node, last_node.
+	Reported by Marc Nieper-Wißkirchen in
+	<https://lists.gnu.org/archive/html/bug-gnulib/2021-04/msg00005.html>.
+	* lib/gl_list.h (gl_list_first_node, gl_list_last_node): New functions.
+	(struct gl_list_implementation): Add members first_node, last_node.
+	* lib/gl_array_list.c (gl_array_first_node, gl_array_last_node): New
+	functions.
+	(gl_array_list_implementation): Add the new operations.
+	* lib/gl_carray_list.c (gl_carray_first_node, gl_carray_last_node): New
+	functions.
+	(gl_carray_list_implementation): Add the new operations.
+	* lib/gl_anylinked_list2.h (gl_linked_first_node, gl_linked_last_node):
+	New functions.
+	* lib/gl_linked_list.c (gl_linked_list_implementation): Add the new
+	operations.
+	* lib/gl_linkedhash_list.c (gl_linkedhash_list_implementation):
+	Likewise.
+	* lib/gl_anytree_list2.h (gl_tree_first_node, gl_tree_last_node): New
+	functions.
+	* lib/gl_avltree_list.c (gl_avltree_list_implementation): Add the new
+	operations.
+	* lib/gl_avltreehash_list.c (gl_avltreehash_list_implementation):
+	Likewise.
+	* lib/gl_rbtree_list.c (gl_rbtree_list_implementation): Likewise.
+	* lib/gl_rbtreehash_list.c (gl_rbtreehash_list_implementation):
+	Likewise.
+	* lib/gl_sublist.c (gl_sublist_first_node, gl_sublist_last_node): New
+	functions.
+	(gl_sublist_list_implementation): Add the new operations.
+	* lib/gl_list.hh (class gl_List): Add member functions first_node,
+	last_node.
+	* doc/containers.texi: Update table.
+
+2021-04-03  Bruno Haible  <br...@clisp.org>
+
 	xalloc-die: Fix compilation error (regression from 2021-03-28).
 	* lib/xalloc.h: Don't include idx.h and xalloc-oversized.h if the module
 	'xalloc' is not in use.
diff --git a/doc/containers.texi b/doc/containers.texi
index cb0c22a..15c915b 100644
--- a/doc/containers.texi
+++ b/doc/containers.texi
@@ -151,6 +151,24 @@ for the ``sequential list'' data type are:
 @tab @math{O(1)}
 @tab @math{O(@log n)}
 @tab @math{O(@log n)}
+@item @code{gl_list_first_node}
+@tab @math{O(1)}
+@tab @math{O(1)}
+@tab @math{O(1)}
+@tab @math{O(@log n)}
+@tab @math{O(1)}
+@tab @math{O(1)}
+@tab @math{O(@log n)}
+@tab @math{O(@log n)}
+@item @code{gl_list_last_node}
+@tab @math{O(1)}
+@tab @math{O(1)}
+@tab @math{O(1)}
+@tab @math{O(@log n)}
+@tab @math{O(1)}
+@tab @math{O(1)}
+@tab @math{O(@log n)}
+@tab @math{O(@log n)}
 @item @code{gl_list_get_at}
 @tab @math{O(1)}
 @tab @math{O(1)}
diff --git a/lib/gl_anylinked_list2.h b/lib/gl_anylinked_list2.h
index 1434d59..6af3f76 100644
--- a/lib/gl_anylinked_list2.h
+++ b/lib/gl_anylinked_list2.h
@@ -229,6 +229,24 @@ gl_linked_previous_node (gl_list_t list, gl_list_node_t node)
   return (node->prev != &list->root ? node->prev : NULL);
 }
 
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_linked_first_node (gl_list_t list)
+{
+  if (list->count > 0)
+    return list->root.next;
+  else
+    return NULL;
+}
+
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_linked_last_node (gl_list_t list)
+{
+  if (list->count > 0)
+    return list->root.prev;
+  else
+    return NULL;
+}
+
 static const void * _GL_ATTRIBUTE_PURE
 gl_linked_get_at (gl_list_t list, size_t position)
 {
diff --git a/lib/gl_anytree_list2.h b/lib/gl_anytree_list2.h
index 59fe01d..fbf514b 100644
--- a/lib/gl_anytree_list2.h
+++ b/lib/gl_anytree_list2.h
@@ -140,6 +140,32 @@ gl_tree_previous_node (gl_list_t list _GL_ATTRIBUTE_MAYBE_UNUSED,
   return node;
 }
 
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_tree_first_node (gl_list_t list _GL_ATTRIBUTE_MAYBE_UNUSED)
+{
+  gl_list_node_t node = list->root;
+
+  if (node != NULL)
+    {
+      while (node->left != NULL)
+        node = node->left;
+    }
+  return node;
+}
+
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_tree_last_node (gl_list_t list _GL_ATTRIBUTE_MAYBE_UNUSED)
+{
+  gl_list_node_t node = list->root;
+
+  if (node != NULL)
+    {
+      while (node->right != NULL)
+        node = node->right;
+    }
+  return node;
+}
+
 /* Returns the node at the given position < gl_tree_size (list).  */
 static gl_list_node_t _GL_ATTRIBUTE_PURE
 node_at (gl_list_node_t root, size_t position)
diff --git a/lib/gl_array_list.c b/lib/gl_array_list.c
index e30b881..203e618 100644
--- a/lib/gl_array_list.c
+++ b/lib/gl_array_list.c
@@ -166,6 +166,24 @@ gl_array_previous_node (gl_list_t list, gl_list_node_t node)
     return NULL;
 }
 
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_array_first_node (gl_list_t list)
+{
+  if (list->count > 0)
+    return INDEX_TO_NODE (0);
+  else
+    return NULL;
+}
+
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_array_last_node (gl_list_t list)
+{
+  if (list->count > 0)
+    return INDEX_TO_NODE (list->count - 1);
+  else
+    return NULL;
+}
+
 static const void * _GL_ATTRIBUTE_PURE
 gl_array_get_at (gl_list_t list, size_t position)
 {
@@ -651,6 +669,8 @@ const struct gl_list_implementation gl_array_list_implementation =
     gl_array_node_nx_set_value,
     gl_array_next_node,
     gl_array_previous_node,
+    gl_array_first_node,
+    gl_array_last_node,
     gl_array_get_at,
     gl_array_nx_set_at,
     gl_array_search_from_to,
diff --git a/lib/gl_avltree_list.c b/lib/gl_avltree_list.c
index 241949a..801b141 100644
--- a/lib/gl_avltree_list.c
+++ b/lib/gl_avltree_list.c
@@ -77,6 +77,8 @@ const struct gl_list_implementation gl_avltree_list_implementation =
     gl_tree_node_nx_set_value,
     gl_tree_next_node,
     gl_tree_previous_node,
+    gl_tree_first_node,
+    gl_tree_last_node,
     gl_tree_get_at,
     gl_tree_nx_set_at,
     gl_tree_search_from_to,
diff --git a/lib/gl_avltreehash_list.c b/lib/gl_avltreehash_list.c
index 5d0be7e..9a96f68 100644
--- a/lib/gl_avltreehash_list.c
+++ b/lib/gl_avltreehash_list.c
@@ -100,6 +100,8 @@ const struct gl_list_implementation gl_avltreehash_list_implementation =
     gl_tree_node_nx_set_value,
     gl_tree_next_node,
     gl_tree_previous_node,
+    gl_tree_first_node,
+    gl_tree_last_node,
     gl_tree_get_at,
     gl_tree_nx_set_at,
     gl_tree_search_from_to,
diff --git a/lib/gl_carray_list.c b/lib/gl_carray_list.c
index a50f093..add4cc3 100644
--- a/lib/gl_carray_list.c
+++ b/lib/gl_carray_list.c
@@ -181,6 +181,24 @@ gl_carray_previous_node (gl_list_t list, gl_list_node_t node)
     return NULL;
 }
 
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_carray_first_node (gl_list_t list)
+{
+  if (list->count > 0)
+    return INDEX_TO_NODE (0);
+  else
+    return NULL;
+}
+
+static gl_list_node_t _GL_ATTRIBUTE_PURE
+gl_carray_last_node (gl_list_t list)
+{
+  if (list->count > 0)
+    return INDEX_TO_NODE (list->count - 1);
+  else
+    return NULL;
+}
+
 static const void * _GL_ATTRIBUTE_PURE
 gl_carray_get_at (gl_list_t list, size_t position)
 {
@@ -844,6 +862,8 @@ const struct gl_list_implementation gl_carray_list_implementation =
     gl_carray_node_nx_set_value,
     gl_carray_next_node,
     gl_carray_previous_node,
+    gl_carray_first_node,
+    gl_carray_last_node,
     gl_carray_get_at,
     gl_carray_nx_set_at,
     gl_carray_search_from_to,
diff --git a/lib/gl_linked_list.c b/lib/gl_linked_list.c
index f46d45b..087968e 100644
--- a/lib/gl_linked_list.c
+++ b/lib/gl_linked_list.c
@@ -38,6 +38,8 @@ const struct gl_list_implementation gl_linked_list_implementation =
     gl_linked_node_nx_set_value,
     gl_linked_next_node,
     gl_linked_previous_node,
+    gl_linked_first_node,
+    gl_linked_last_node,
     gl_linked_get_at,
     gl_linked_nx_set_at,
     gl_linked_search_from_to,
diff --git a/lib/gl_linkedhash_list.c b/lib/gl_linkedhash_list.c
index 2c76bae..70eca52 100644
--- a/lib/gl_linkedhash_list.c
+++ b/lib/gl_linkedhash_list.c
@@ -86,6 +86,8 @@ const struct gl_list_implementation gl_linkedhash_list_implementation =
     gl_linked_node_nx_set_value,
     gl_linked_next_node,
     gl_linked_previous_node,
+    gl_linked_first_node,
+    gl_linked_last_node,
     gl_linked_get_at,
     gl_linked_nx_set_at,
     gl_linked_search_from_to,
diff --git a/lib/gl_list.h b/lib/gl_list.h
index 3c1aede..7fc22bb 100644
--- a/lib/gl_list.h
+++ b/lib/gl_list.h
@@ -73,6 +73,8 @@ extern "C" {
    gl_list_node_set_value      O(1)     O(1)     O(1)      O(1)    O((log n)²)/O(1)
    gl_list_next_node           O(1)     O(1)   O(log n)    O(1)       O(log n)
    gl_list_previous_node       O(1)     O(1)   O(log n)    O(1)       O(log n)
+   gl_list_first_node          O(1)     O(1)   O(log n)    O(1)       O(log n)
+   gl_list_last_node           O(1)     O(1)   O(log n)    O(1)       O(log n)
    gl_list_get_at              O(1)     O(n)   O(log n)    O(n)       O(log n)
    gl_list_get_first           O(1)     O(1)   O(log n)    O(1)       O(log n)
    gl_list_get_last            O(1)     O(1)   O(log n)    O(1)       O(log n)
@@ -207,6 +209,23 @@ extern gl_list_node_t gl_list_next_node (gl_list_t list, gl_list_node_t node);
    if the given node is the first (leftmost) one in the list.  */
 extern gl_list_node_t gl_list_previous_node (gl_list_t list, gl_list_node_t node);
 
+/* Returns the first node in the list, or NULL if the list is empty.
+   This function is useful for iterating through the list like this:
+     gl_list_node_t node;
+     for (node = gl_list_first_node (list); node != NULL; node = gl_list_next_node (node))
+       ...
+ */
+extern gl_list_node_t gl_list_first_node (gl_list_t list);
+
+/* Returns the last node in the list, or NULL if the list is empty.
+   This function is useful for iterating through the list in backward order,
+   like this:
+     gl_list_node_t node;
+     for (node = gl_list_last_node (list); node != NULL; node = gl_list_previous_node (node))
+       ...
+ */
+extern gl_list_node_t gl_list_last_node (gl_list_t list);
+
 /* Returns the element at a given position in the list.
    POSITION must be >= 0 and < gl_list_size (list).  */
 extern const void * gl_list_get_at (gl_list_t list, size_t position);
@@ -507,6 +526,8 @@ struct gl_list_implementation
                             const void *elt);
   gl_list_node_t (*next_node) (gl_list_t list, gl_list_node_t node);
   gl_list_node_t (*previous_node) (gl_list_t list, gl_list_node_t node);
+  gl_list_node_t (*first_node) (gl_list_t list);
+  gl_list_node_t (*last_node) (gl_list_t list);
   const void * (*get_at) (gl_list_t list, size_t position);
   gl_list_node_t (*nx_set_at) (gl_list_t list, size_t position,
                                const void *elt);
@@ -631,6 +652,20 @@ gl_list_previous_node (gl_list_t list, gl_list_node_t node)
          ->previous_node (list, node);
 }
 
+GL_LIST_INLINE gl_list_node_t
+gl_list_first_node (gl_list_t list)
+{
+  return ((const struct gl_list_impl_base *) list)->vtable
+         ->first_node (list);
+}
+
+GL_LIST_INLINE gl_list_node_t
+gl_list_last_node (gl_list_t list)
+{
+  return ((const struct gl_list_impl_base *) list)->vtable
+         ->last_node (list);
+}
+
 GL_LIST_INLINE const void *
 gl_list_get_at (gl_list_t list, size_t position)
 {
diff --git a/lib/gl_list.hh b/lib/gl_list.hh
index 7bff5dd..af2ca67 100644
--- a/lib/gl_list.hh
+++ b/lib/gl_list.hh
@@ -134,6 +134,14 @@ public:
   gl_list_node_t previous_node (gl_list_node_t node) const
     { return gl_list_previous_node (_ptr, node); }
 
+  /* Returns the first node in the list, or NULL if the list is empty.  */
+  gl_list_node_t first_node () const
+    { return gl_list_first_node (_ptr); }
+
+  /* Returns the last node in the list, or NULL if the list is empty.  */
+  gl_list_node_t last_node () const
+    { return gl_list_last_node (_ptr); }
+
   /* Returns the element at a given position in the list.
      POSITION must be >= 0 and < gl_list_size (list).  */
   ELTYPE * get_at (size_t position) const
diff --git a/lib/gl_rbtree_list.c b/lib/gl_rbtree_list.c
index 185ed82..9a053b4 100644
--- a/lib/gl_rbtree_list.c
+++ b/lib/gl_rbtree_list.c
@@ -78,6 +78,8 @@ const struct gl_list_implementation gl_rbtree_list_implementation =
     gl_tree_node_nx_set_value,
     gl_tree_next_node,
     gl_tree_previous_node,
+    gl_tree_first_node,
+    gl_tree_last_node,
     gl_tree_get_at,
     gl_tree_nx_set_at,
     gl_tree_search_from_to,
diff --git a/lib/gl_rbtreehash_list.c b/lib/gl_rbtreehash_list.c
index c4a3c7d..d8fcb8e 100644
--- a/lib/gl_rbtreehash_list.c
+++ b/lib/gl_rbtreehash_list.c
@@ -101,6 +101,8 @@ const struct gl_list_implementation gl_rbtreehash_list_implementation =
     gl_tree_node_nx_set_value,
     gl_tree_next_node,
     gl_tree_previous_node,
+    gl_tree_first_node,
+    gl_tree_last_node,
     gl_tree_get_at,
     gl_tree_nx_set_at,
     gl_tree_search_from_to,
diff --git a/lib/gl_sublist.c b/lib/gl_sublist.c
index bdd7d50..63359b8 100644
--- a/lib/gl_sublist.c
+++ b/lib/gl_sublist.c
@@ -122,6 +122,25 @@ gl_sublist_previous_node (gl_list_t list, gl_list_node_t node)
     return NULL;
 }
 
+static gl_list_node_t
+gl_sublist_first_node (gl_list_t list)
+{
+  if (list->end > list->start)
+    return INDEX_TO_NODE (0);
+  else
+    return NULL;
+}
+
+static gl_list_node_t
+gl_sublist_last_node (gl_list_t list)
+{
+  size_t count = list->end - list->start;
+  if (count > 0)
+    return INDEX_TO_NODE (count - 1);
+  else
+    return NULL;
+}
+
 static const void *
 gl_sublist_get_at (gl_list_t list, size_t position)
 {
@@ -413,6 +432,8 @@ static const struct gl_list_implementation gl_sublist_list_implementation =
     gl_sublist_node_nx_set_value,
     gl_sublist_next_node,
     gl_sublist_previous_node,
+    gl_sublist_first_node,
+    gl_sublist_last_node,
     gl_sublist_get_at,
     gl_sublist_nx_set_at,
     gl_sublist_search_from_to,
-- 
2.7.4

From 781f81012720e7f9f4c50630f37dfc73e2ad4906 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sat, 3 Apr 2021 18:25:56 +0200
Subject: [PATCH 2/2] *-list tests: Add more tests.

* tests/test-array_list.c (check_equals_by_forward_iteration,
check_equals_by_backward_iteration): New functions.
(main): Invoke them.
* tests/test-carray_list.c: Likewise.
* tests/test-linked_list.c: Likewise.
* tests/test-linkedhash_list.c: Likewise.
* tests/test-avltree_list.c: Likewise.
* tests/test-avltreehash_list.c: Likewise.
* tests/test-rbtree_list.c: Likewise.
* tests/test-rbtreehash_list.c: Likewise.
---
 ChangeLog                     | 12 ++++++++++++
 tests/test-array_list.c       | 39 +++++++++++++++++++++++++++++++++++++++
 tests/test-avltree_list.c     | 33 +++++++++++++++++++++++++++++++++
 tests/test-avltreehash_list.c | 33 +++++++++++++++++++++++++++++++++
 tests/test-carray_list.c      | 33 +++++++++++++++++++++++++++++++++
 tests/test-linked_list.c      | 33 +++++++++++++++++++++++++++++++++
 tests/test-linkedhash_list.c  | 33 +++++++++++++++++++++++++++++++++
 tests/test-rbtree_list.c      | 33 +++++++++++++++++++++++++++++++++
 tests/test-rbtreehash_list.c  | 33 +++++++++++++++++++++++++++++++++
 9 files changed, 282 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 958ad02..117ea44 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2021-04-03  Bruno Haible  <br...@clisp.org>
 
+	*-list tests: Add more tests.
+	* tests/test-array_list.c (check_equals_by_forward_iteration,
+	check_equals_by_backward_iteration): New functions.
+	(main): Invoke them.
+	* tests/test-carray_list.c: Likewise.
+	* tests/test-linked_list.c: Likewise.
+	* tests/test-linkedhash_list.c: Likewise.
+	* tests/test-avltree_list.c: Likewise.
+	* tests/test-avltreehash_list.c: Likewise.
+	* tests/test-rbtree_list.c: Likewise.
+	* tests/test-rbtreehash_list.c: Likewise.
+
 	list: Add operations first_node, last_node.
 	Reported by Marc Nieper-Wißkirchen in
 	<https://lists.gnu.org/archive/html/bug-gnulib/2021-04/msg00005.html>.
diff --git a/tests/test-array_list.c b/tests/test-array_list.c
index 1c736ee..fa60691 100644
--- a/tests/test-array_list.c
+++ b/tests/test-array_list.c
@@ -44,6 +44,42 @@ check_equals (gl_list_t list1, gl_list_t list2)
     }
 }
 
+static void
+check_equals_by_forward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  size_t n = gl_list_size (list1);
+  size_t i;
+  gl_list_node_t node2;
+
+  i = 0;
+  node2 = gl_list_first_node (list2);
+  while (i < n && node2 != NULL)
+    {
+      ASSERT (gl_list_get_at (list1, i) == gl_list_node_value (list2, node2));
+      i++;
+      node2 = gl_list_next_node (list2, node2);
+    }
+  ASSERT ((i == n) == (node2 == NULL));
+}
+
+static void
+check_equals_by_backward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  size_t n = gl_list_size (list1);
+  size_t i;
+  gl_list_node_t node2;
+
+  i = n - 1;
+  node2 = gl_list_last_node (list2);
+  while (i != (size_t)(-1) && node2 != NULL)
+    {
+      ASSERT (gl_list_get_at (list1, i) == gl_list_node_value (list2, node2));
+      i--;
+      node2 = gl_list_previous_node (list2, node2);
+    }
+  ASSERT ((i == (size_t)(-1)) == (node2 == NULL));
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -75,6 +111,9 @@ main (int argc, char *argv[])
 
     check_equals (list1, list2);
 
+    check_equals_by_forward_iteration (list1, list2);
+    check_equals_by_backward_iteration (list1, list2);
+
     for (repeat = 0; repeat < 10000; repeat++)
       {
         unsigned int operation = RANDOM (18);
diff --git a/tests/test-avltree_list.c b/tests/test-avltree_list.c
index c4c0f02..b4938c4 100644
--- a/tests/test-avltree_list.c
+++ b/tests/test-avltree_list.c
@@ -48,6 +48,36 @@ check_equals (gl_list_t list1, gl_list_t list2)
 }
 
 static void
+check_equals_by_forward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_first_node (list1);
+  gl_list_node_t node2 = gl_list_first_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_next_node (list1, node1);
+      node2 = gl_list_next_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
+check_equals_by_backward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_last_node (list1);
+  gl_list_node_t node2 = gl_list_last_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_previous_node (list1, node1);
+      node2 = gl_list_previous_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
 check_all (gl_list_t list1, gl_list_t list2, gl_list_t list3)
 {
   gl_avltree_list_check_invariants (list2);
@@ -92,6 +122,9 @@ main (int argc, char *argv[])
 
     check_all (list1, list2, list3);
 
+    check_equals_by_forward_iteration (list1, list2);
+    check_equals_by_backward_iteration (list1, list2);
+
     for (repeat = 0; repeat < 10000; repeat++)
       {
         unsigned int operation = RANDOM (18);
diff --git a/tests/test-avltreehash_list.c b/tests/test-avltreehash_list.c
index a77ee86..040efd3 100644
--- a/tests/test-avltreehash_list.c
+++ b/tests/test-avltreehash_list.c
@@ -75,6 +75,36 @@ check_equals (gl_list_t list1, gl_list_t list2)
 }
 
 static void
+check_equals_by_forward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_first_node (list1);
+  gl_list_node_t node2 = gl_list_first_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_next_node (list1, node1);
+      node2 = gl_list_next_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
+check_equals_by_backward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_last_node (list1);
+  gl_list_node_t node2 = gl_list_last_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_previous_node (list1, node1);
+      node2 = gl_list_previous_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
 check_all (gl_list_t list1, gl_list_t list2, gl_list_t list3)
 {
   gl_avltreehash_list_check_invariants (list2);
@@ -122,6 +152,9 @@ main (int argc, char *argv[])
 
     check_all (list1, list2, list3);
 
+    check_equals_by_forward_iteration (list1, list2);
+    check_equals_by_backward_iteration (list1, list2);
+
     for (repeat = 0; repeat < 10000; repeat++)
       {
         unsigned int operation = RANDOM (18);
diff --git a/tests/test-carray_list.c b/tests/test-carray_list.c
index 53ab6f5..4077571 100644
--- a/tests/test-carray_list.c
+++ b/tests/test-carray_list.c
@@ -46,6 +46,36 @@ check_equals (gl_list_t list1, gl_list_t list2)
 }
 
 static void
+check_equals_by_forward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_first_node (list1);
+  gl_list_node_t node2 = gl_list_first_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_next_node (list1, node1);
+      node2 = gl_list_next_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
+check_equals_by_backward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_last_node (list1);
+  gl_list_node_t node2 = gl_list_last_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_previous_node (list1, node1);
+      node2 = gl_list_previous_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
 check_all (gl_list_t list1, gl_list_t list2, gl_list_t list3)
 {
   check_equals (list1, list2);
@@ -88,6 +118,9 @@ main (int argc, char *argv[])
 
     check_all (list1, list2, list3);
 
+    check_equals_by_forward_iteration (list1, list2);
+    check_equals_by_backward_iteration (list1, list2);
+
     for (repeat = 0; repeat < 10000; repeat++)
       {
         unsigned int operation = RANDOM (18);
diff --git a/tests/test-linked_list.c b/tests/test-linked_list.c
index 4f3ec5d..fff7e06 100644
--- a/tests/test-linked_list.c
+++ b/tests/test-linked_list.c
@@ -46,6 +46,36 @@ check_equals (gl_list_t list1, gl_list_t list2)
 }
 
 static void
+check_equals_by_forward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_first_node (list1);
+  gl_list_node_t node2 = gl_list_first_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_next_node (list1, node1);
+      node2 = gl_list_next_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
+check_equals_by_backward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_last_node (list1);
+  gl_list_node_t node2 = gl_list_last_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_previous_node (list1, node1);
+      node2 = gl_list_previous_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
 check_all (gl_list_t list1, gl_list_t list2, gl_list_t list3)
 {
   check_equals (list1, list2);
@@ -88,6 +118,9 @@ main (int argc, char *argv[])
 
     check_all (list1, list2, list3);
 
+    check_equals_by_forward_iteration (list1, list2);
+    check_equals_by_backward_iteration (list1, list2);
+
     for (repeat = 0; repeat < 10000; repeat++)
       {
         unsigned int operation = RANDOM (18);
diff --git a/tests/test-linkedhash_list.c b/tests/test-linkedhash_list.c
index b61feda..fc2a8a05 100644
--- a/tests/test-linkedhash_list.c
+++ b/tests/test-linkedhash_list.c
@@ -73,6 +73,36 @@ check_equals (gl_list_t list1, gl_list_t list2)
 }
 
 static void
+check_equals_by_forward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_first_node (list1);
+  gl_list_node_t node2 = gl_list_first_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_next_node (list1, node1);
+      node2 = gl_list_next_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
+check_equals_by_backward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_last_node (list1);
+  gl_list_node_t node2 = gl_list_last_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_previous_node (list1, node1);
+      node2 = gl_list_previous_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
 check_all (gl_list_t list1, gl_list_t list2, gl_list_t list3)
 {
   check_equals (list1, list2);
@@ -118,6 +148,9 @@ main (int argc, char *argv[])
 
     check_all (list1, list2, list3);
 
+    check_equals_by_forward_iteration (list1, list2);
+    check_equals_by_backward_iteration (list1, list2);
+
     for (repeat = 0; repeat < 10000; repeat++)
       {
         unsigned int operation = RANDOM (18);
diff --git a/tests/test-rbtree_list.c b/tests/test-rbtree_list.c
index 9c403f7..d3ea656 100644
--- a/tests/test-rbtree_list.c
+++ b/tests/test-rbtree_list.c
@@ -48,6 +48,36 @@ check_equals (gl_list_t list1, gl_list_t list2)
 }
 
 static void
+check_equals_by_forward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_first_node (list1);
+  gl_list_node_t node2 = gl_list_first_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_next_node (list1, node1);
+      node2 = gl_list_next_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
+check_equals_by_backward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_last_node (list1);
+  gl_list_node_t node2 = gl_list_last_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_previous_node (list1, node1);
+      node2 = gl_list_previous_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
 check_all (gl_list_t list1, gl_list_t list2, gl_list_t list3)
 {
   gl_rbtree_list_check_invariants (list2);
@@ -92,6 +122,9 @@ main (int argc, char *argv[])
 
     check_all (list1, list2, list3);
 
+    check_equals_by_forward_iteration (list1, list2);
+    check_equals_by_backward_iteration (list1, list2);
+
     for (repeat = 0; repeat < 10000; repeat++)
       {
         unsigned int operation = RANDOM (18);
diff --git a/tests/test-rbtreehash_list.c b/tests/test-rbtreehash_list.c
index 41ef5c4..89fc3eb 100644
--- a/tests/test-rbtreehash_list.c
+++ b/tests/test-rbtreehash_list.c
@@ -75,6 +75,36 @@ check_equals (gl_list_t list1, gl_list_t list2)
 }
 
 static void
+check_equals_by_forward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_first_node (list1);
+  gl_list_node_t node2 = gl_list_first_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_next_node (list1, node1);
+      node2 = gl_list_next_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
+check_equals_by_backward_iteration (gl_list_t list1, gl_list_t list2)
+{
+  gl_list_node_t node1 = gl_list_last_node (list1);
+  gl_list_node_t node2 = gl_list_last_node (list2);
+  while (node1 != NULL && node2 != NULL)
+    {
+      ASSERT (gl_list_node_value (list1, node1)
+              == gl_list_node_value (list2, node2));
+      node1 = gl_list_previous_node (list1, node1);
+      node2 = gl_list_previous_node (list2, node2);
+    }
+  ASSERT ((node1 == NULL) == (node2 == NULL));
+}
+
+static void
 check_all (gl_list_t list1, gl_list_t list2, gl_list_t list3)
 {
   gl_rbtreehash_list_check_invariants (list2);
@@ -122,6 +152,9 @@ main (int argc, char *argv[])
 
     check_all (list1, list2, list3);
 
+    check_equals_by_forward_iteration (list1, list2);
+    check_equals_by_backward_iteration (list1, list2);
+
     for (repeat = 0; repeat < 10000; repeat++)
       {
         unsigned int operation = RANDOM (18);
-- 
2.7.4

Reply via email to