renamings and add of singly linked list
authorChris Koeritz <fred@gruntose.com>
Sun, 1 Jan 2017 18:12:52 +0000 (13:12 -0500)
committerChris Koeritz <fred@gruntose.com>
Sun, 1 Jan 2017 18:12:52 +0000 (13:12 -0500)
fixed name of "list" class to be doubly linked list, since it is that.  now kind of grossed out by doubly linked list's implementation; suggest overhauling it so dl list IS a node in the dl list, rather than just a manager of node objects.  also noting it has a gross iterator implementation, but it appears to be critical to the design of the class, so try to leave it intact.
added a very basic singly linked list in order to have the new cycle detection algorithm.  added a test for singly linked lists.
added definition for eclipse on gnu unix of __UNIX__, which calmed a lot of complaints about HOOPLE_MAIN and other memset/strncpy/etc methods.

12 files changed:
nucleus/.cproject
nucleus/library/nodes/doubly_linked_list.cpp [new file with mode: 0644]
nucleus/library/nodes/doubly_linked_list.h [new file with mode: 0644]
nucleus/library/nodes/list.cpp [deleted file]
nucleus/library/nodes/list.h [deleted file]
nucleus/library/nodes/makefile
nucleus/library/nodes/singly_linked_list.cpp [new file with mode: 0644]
nucleus/library/nodes/singly_linked_list.h [new file with mode: 0644]
nucleus/library/tests_nodes/makefile
nucleus/library/tests_nodes/test_doubly_linked_list.cpp [new file with mode: 0644]
nucleus/library/tests_nodes/test_list.cpp [deleted file]
nucleus/library/tests_nodes/test_singly_linked_list.cpp [new file with mode: 0644]

index d05aaa7983f50c564efc04513132211009b14557..858a68d5919ec5699ad7639ca903154e42cf8947 100644 (file)
                                                        <builder arguments="-c '(make -I &quot;${workspace_loc:/feisty_meow.nucleus}/../scripts/clam&quot; 2&gt;&amp;1 | grep -v &quot;deps: No such&quot;)'" command="bash" id="cdt.managedbuild.target.gnu.builder.base.397021136" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
                                                        <tool id="cdt.managedbuild.tool.gnu.archiver.base.536141204" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
                                                        <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.140168967" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
+                                                               <option id="gnu.cpp.compiler.option.preprocessor.def.786915406" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+                                                                       <listOptionValue builtIn="false" value="__UNIX__"/>
+                                                               </option>
                                                                <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1783803771" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
                                                        </tool>
                                                        <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1489239604" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
+                                                               <option id="gnu.c.compiler.option.preprocessor.def.symbols.1839703248" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols">
+                                                                       <listOptionValue builtIn="false" value="__UNIX__"/>
+                                                               </option>
                                                                <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1083415270" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
                                                        </tool>
                                                        <tool id="cdt.managedbuild.tool.gnu.c.linker.base.458164275" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
diff --git a/nucleus/library/nodes/doubly_linked_list.cpp b/nucleus/library/nodes/doubly_linked_list.cpp
new file mode 100644 (file)
index 0000000..27c2886
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+*
+*  Name   : doubly_linked_list
+*  Author : Chris Koeritz
+**
+* Copyright (c) 1998-$now By Author.  This program is free software; you can
+* redistribute it and/or modify it under the terms of the GNU General Public
+* License as published by the Free Software Foundation; either version 2 of
+* the License or (at your option) any later version.  This is online at:
+*     http://www.fsf.org/copyleft/gpl.html
+* Please send any updates to: fred@gruntose.com
+*/
+
+/*
+ * POLICIES:
+ *
+ * + the cursor should never be stored to or deleted; it is merely a scanner that runs through the list.
+ * + the cursor can point at the head or tail.  any storage action is taken to mean that it applies to the closest real object, if one exists.  any query action is taken similarly.
+ */
+
+#include "doubly_linked_list.h"
+
+#include "node.h"
+
+#include <basis/functions.h>
+
+namespace nodes {
+
+// nice names for the positions of the next link and the previous link in
+// our node's indices.
+const int PREVIOUS = 0;
+const int NEXT = 1;
+
+//////////////
+
+// iterator functions:
+
+void doubly_linked_list::iterator::operator ++()
+{
+  if (is_tail()) return;  // already at the end.
+  _cursor = _cursor->get_link(NEXT);
+}
+
+void doubly_linked_list::iterator::operator --()
+{
+  if (is_head()) return;  // already at the front.
+  _cursor = _cursor->get_link(PREVIOUS);
+}
+
+bool doubly_linked_list::iterator::operator ==(const iterator &to_compare) const
+{ return _cursor == to_compare._cursor; }
+
+const node *doubly_linked_list::iterator::observe()
+{
+  if (!_manager || _manager->empty()) return NULL_POINTER;
+  if (*this == _manager->head()) next();
+  if (*this == _manager->tail()) previous();
+  return _cursor;
+}
+
+node *doubly_linked_list::iterator::access()
+{
+  if (!_manager || _manager->empty()) return NULL_POINTER;
+  if (*this == _manager->head()) next();
+  if (*this == _manager->tail()) previous();
+  return _cursor;
+}
+
+bool doubly_linked_list::iterator::is_head() const
+{
+  if (!_manager) return false;
+  return _cursor == _manager->_head;
+}
+
+bool doubly_linked_list::iterator::is_tail() const
+{ 
+  if (!_manager) return false;
+  return _cursor == _manager->_tail;
+}
+
+void doubly_linked_list::iterator::jump_head()
+{
+  if (!_manager) return;
+  _cursor = _manager->_head;
+}
+
+void doubly_linked_list::iterator::jump_tail()
+{
+  if (!_manager) return;
+  _cursor = _manager->_tail;
+}
+
+//////////////
+
+doubly_linked_list::doubly_linked_list()
+: _head(NULL_POINTER), _tail(NULL_POINTER)
+{
+  _head = new node(2);
+  _tail = new node(2);
+  _head->set_link(NEXT, _tail);
+  _tail->set_link(PREVIOUS, _head);
+}
+
+doubly_linked_list::~doubly_linked_list()
+{
+  iterator zapper = head();
+  while (!empty())
+    zap(zapper);
+  WHACK(_head);
+  WHACK(_tail);
+}
+
+bool doubly_linked_list::empty() const
+{
+  if (_head->get_link(NEXT) == _tail) return true;
+  return false;
+}
+
+bool doubly_linked_list::set_index(iterator &where, int new_index)
+{
+  if (where._manager != this) return false;
+  if (empty()) return false;
+  node *skipper = _head->get_link(NEXT);
+  for (int i = 0; i < new_index; i++) {
+    skipper = skipper->get_link(NEXT);
+    if (skipper == _tail) return false;  // out of bounds now.
+  }
+  where._cursor = skipper;
+  return true;
+}
+
+bool doubly_linked_list::forward(iterator &where, int count)
+{ 
+  if (where._manager != this) return false;
+  if (count <= 0) return true;
+  if (items_from_tail(where) < count) return false;
+  if (where.is_head()) where.next();  // skip the head guard.
+  for (int i = 0; i < count; i++) where.next();
+  return true;
+}
+
+bool doubly_linked_list::backward(iterator &where, int count)
+{
+  if (where._manager != this) return false;
+  if (count <= 0) return true;
+  if (items_from_head(where) < count) return false;
+  if (where.is_tail()) where.previous();  // skip the tail guard.
+  for (int i = 0; i < count; i++) where.previous();
+  return true;
+}
+
+int doubly_linked_list::elements() const
+{
+  if (empty()) return 0;
+  int to_return = 0;
+  node *skipper = _head->get_link(NEXT);
+  while (skipper != _tail) {
+    to_return++;
+    skipper = skipper->get_link(NEXT);
+  }
+  return to_return;
+}
+
+int doubly_linked_list::items_from_head(const iterator &where) const
+{
+  if (where._manager != this) return 0;
+  if (where.is_head()) return 0;  // make sure it's not there already.
+  int index = 0;
+  node *skipper = _head->get_link(NEXT);
+  while ( (where._cursor != skipper) && (skipper != _tail) ) {
+    index++;
+    skipper = skipper->get_link(NEXT);
+  }
+  return index;
+}
+
+int doubly_linked_list::items_from_tail(const iterator &where) const
+{
+  if (where._manager != this) return 0;
+  if (where.is_tail()) return 0;  // make sure it's not there already.
+  int index = 0;
+  node *skipper = _tail->get_link(PREVIOUS);
+  while ( (where._cursor != skipper) && (skipper != _head) ) {
+    index++;
+    skipper = skipper->get_link(PREVIOUS);
+  }
+  return index;
+}
+
+/*
+node *list::get()
+{
+  if (empty()) return NULL_POINTER;  // make sure the list isn't empty.
+  if (_cursor == _head) return _head->get_link(NEXT);
+    // check special case for pointing at the head.
+  if (_cursor == _tail) return _tail->get_link(PREVIOUS);
+    // check special case for pointing at the tail.
+  return _cursor;
+}
+*/
+
+node *doubly_linked_list::remove(iterator &where)
+{
+  if (where._manager != this) return NULL_POINTER;
+  if (empty()) return NULL_POINTER;
+  if (where._cursor == _head)
+    where._cursor = _head->get_link(NEXT);
+  if (where._cursor == _tail)
+    where._cursor = _tail->get_link(PREVIOUS);
+  node *old_cursor = where._cursor;
+  node *old_previous = old_cursor->get_link(PREVIOUS);
+  node *old_next = old_cursor->get_link(NEXT);
+  old_cursor->set_link(PREVIOUS, NULL_POINTER);
+  old_cursor->set_link(NEXT, NULL_POINTER);
+  old_previous->set_link(NEXT, old_next);
+  old_next->set_link(PREVIOUS, old_previous);
+  where._cursor = old_next;
+  return old_cursor;
+}
+
+void doubly_linked_list::zap(iterator &where) { delete remove(where); }
+
+void doubly_linked_list::append(iterator &where, node *new_node)
+{
+  if (where._manager != this) return;
+  while (new_node->links() < 2) new_node->insert_link(0, NULL_POINTER);
+  if (empty()) where._cursor = _head;
+  if (where._cursor == _tail)
+    where._cursor = _tail->get_link(PREVIOUS);
+    // shift from the tail sentinel to the tail element.
+  node *save_next = where._cursor->get_link(NEXT);
+  where._cursor->set_link(NEXT, new_node);
+  new_node->set_link(PREVIOUS, where._cursor);
+  new_node->set_link(NEXT, save_next);
+  save_next->set_link(PREVIOUS, new_node);
+  where._cursor = new_node;
+}
+
+void doubly_linked_list::insert(iterator &where, node *new_node)
+{
+  if (where._manager != this) return;
+  while (new_node->links() < 2) new_node->insert_link(0, NULL_POINTER);
+  if (empty()) where._cursor = _tail;
+  if (where._cursor == _head)
+    where._cursor = _head->get_link(NEXT);
+    // shift from head sentinel to the head element.
+  node *save_prior = where._cursor->get_link(PREVIOUS);
+  where._cursor->set_link(PREVIOUS, new_node);
+  new_node->set_link(NEXT, where._cursor);
+  new_node->set_link(PREVIOUS, save_prior);
+  save_prior->set_link(NEXT, new_node);
+  where._cursor = new_node;
+}
+
+void doubly_linked_list::zap_all()
+{
+  iterator zapper = head();
+  while (!empty()) zap(zapper);
+}
+
+} // namespace.
+
+
+
+
diff --git a/nucleus/library/nodes/doubly_linked_list.h b/nucleus/library/nodes/doubly_linked_list.h
new file mode 100644 (file)
index 0000000..987e248
--- /dev/null
@@ -0,0 +1,194 @@
+#ifndef DOUBLY_LINKED_LIST_CLASS
+#define DOUBLY_LINKED_LIST_CLASS
+
+/*
+*  Name   : doubly_linked_list
+*  Author : Chris Koeritz
+**
+* Copyright (c) 1998-$now By Author.  This program is free software; you can
+* redistribute it and/or modify it under the terms of the GNU General Public
+* License as published by the Free Software Foundation; either version 2 of
+* the License or (at your option) any later version.  This is online at:
+*     http://www.fsf.org/copyleft/gpl.html
+* Please send any updates to: fred@gruntose.com
+*/
+
+namespace nodes {
+
+class node;  // forward.
+
+//! Implements a guarded, doubly linked list structure.
+/*!
+  The list is viewed one element at a time, through the monocular of an
+  iterator, which keeps track of the current position in the list.  the
+  iterator's cursor can be shifted around at will.  nodes can be added to
+  the list before or after the cursor, and the node pointed at by the cursor
+  can be queried or modified.  Using multiple iterators is fine as long as
+  you guarantee that they either are all just reading the list or that you
+  have a valid access pattern of reads and writes such that no iterator's
+  cursor is affected.  Note that this list is not thread safe.
+*/
+
+
+//hmmm: is this class really for real?  it's doing all sorts of stuff with nodes, rather than with the list object itself.
+// consider dropping the current implementation and providing more standard list operations, on an object that is actually using itself in its definition, rather than a different class (node).
+// this would be easy to do if we just break down and define the dl list as a node itself.  then we have something to work with.
+
+//current iterator implementation here is bunk.
+
+
+class doubly_linked_list
+{
+public:
+  doubly_linked_list();
+    //!< constructs a blank list.
+
+  ~doubly_linked_list();
+    //!< invalidates all contents of the list and destroys all child nodes.
+
+  int elements() const;
+    //!< returns the number of items currently in the list.
+    /*!< this is a costly operation. */
+
+  bool empty() const;
+    //!< returns true if the list is empty.
+    /*!< this is really quick compared to checking the number of elements. */
+
+  //! iterators allow the list to be traversed.
+  /*! NOTE: it is an error to use an iterator on one list with a different
+  list; the methods will do nothing or return empty results in this
+  situation. */
+
+  class iterator {
+  public:
+    iterator(const doubly_linked_list *mgr, node *start) : _cursor(start), _manager(mgr) {}
+      //!< opens up an iterator on a list.
+      /*!< the preferred method to construct an iterator is to use the
+      head/tail functions in list. */
+
+    void operator ++();  //!< go to next item.
+    void operator --();  //!< go to previous item.
+    void operator ++(int) { ++(*this); }  //!< post-fix synonym for ++.
+    void operator --(int) { --(*this); }  //!< post-fix synonym for --.
+
+  
+    void next() { (*this)++; }  //!< synonym for ++.
+    void previous() { (*this)--; }  //!< synonyms for --.
+
+    bool operator ==(const iterator &to_compare) const;
+      //!< returns true if the two iterators are at the same position.
+
+    bool is_head() const;
+      //!< returns true if the cursor is at the head of the list.
+      /*!< Note: head() and tail() only return true if the iterator is
+      actually positioned _at_ the head or tail guard.  for example, if the
+      cursor is pointing at the last actual element in the list (but not at
+      the guard itself), then is_tail() returns false.  so, in iteration,
+      your iterator will actually go past the last valid element before
+      is_tail() returns true.  thus it is very important to check is_tail()
+      *before* looking at the node with observe() or access(), since those
+      two will shift the list position to the nearest valid node and away
+      from the guard. */
+    bool is_tail() const;
+      //!< returns true if the cursor is at the tail of the list.
+
+    void jump_head();  //!< set the iterator to the head.
+    void jump_tail();  //!< set the iterator to the tail.
+
+    const node *observe();  //!< peek at the current element.
+      /*!< Note: observe() and access() will return the first element if the
+      list is positioned at the head (or the last if at the tail), and will
+      not return NULL_POINTER for these two positions as long as the list has some
+      elements in it.  the cursor will also have been moved to point at the
+      element that is returned.
+      Another Note: it is extremely important that you do not mess with the
+      links owned by the node (or at least the first two links).
+      A Third Note: these two functions can return NULL_POINTER if the list is empty. */
+    node *access();  //!< obtain access to the current element.
+
+  private:
+    node *_cursor;  //!< the current position.
+    friend class doubly_linked_list;  //!< lists have full access to this object.
+    const doubly_linked_list *_manager;  //!< our friendly manager.
+  };
+
+  iterator head() const { return iterator(this, _head); }
+    //!< returns an iterator located at the head of the list.
+  iterator tail() const { return iterator(this, _tail); }
+    //!< returns an iterator located at the tail of the list.
+
+  int index(const iterator &it) const { return items_from_head(it); }
+    //!< returns the zero-based index of the cursor's position from the head.
+    /*!< this is a synonym for items_from_head(). */
+
+  bool set_index(iterator &to_set, int new_index);
+    //!< positions the iterator "to_set" at the index specified.
+
+  // storage and retrieval actions.
+  // Note: all of these methods shift the iterator onto the next valid node
+  // if it is positioned at the beginning or end of the list.
+
+  void append(iterator &where, node *new_node);
+    //!< adds a "new_node" into the list immediately after "where".
+    /*!< the nodes previously following the current node (if any) will follow
+    after the "new_node".  this does not change the current position.
+    ownership of "new_node" is taken over by the list. */
+
+  void insert(iterator &where, node *new_node);
+    //!< places a "new_node" immediately before the current node in the list.
+    /*!< the "new_node" will follow the prior precursor to the current node.
+    this does not change the current position.  ownership of "new_node"
+    is taken over by the list.  after the call, the iterator points at
+    the new node. */
+
+  node *remove(iterator &where);
+    //!< extracts the current item from "where" and repairs the hole.
+    /*!< NULL_POINTER is returned if the list was empty.  the current position is set
+    to the node that used to be after the node that has been removed.  after
+    the call, the iterator points at the new node. */
+
+  void zap(iterator &where);
+    //!< wipes out the item at "where", including its contents.
+    /*!< the current position is reset to the item after the now defunct
+    item (or the tail). */
+
+  void zap_all();
+    //!< destroys all the list elements and makes the list empty.
+    /*!< any existing iterators will be invalidated by this. */
+
+  // the following positioning functions could fail if the request is out of
+  // bounds.  for example, forward cannot go beyond the end of the list.  in
+  // such cases, false is returned and the list pointer is not moved.
+
+  bool forward(iterator &where, int count);
+    //!< moves the list pointer "count" items towards the tail.
+    /*!< Note: forward and backward operate on elements; the head and tail
+    guard are not included in the count.  Also, negative values of "count"
+    are ignored. */
+  bool backward(iterator &where, int count);
+    //!< moves the list pointer "count" items towards the head.
+
+  int items_from_head(const iterator &where) const;
+    //!< Returns the number of elements between the current item and the head.
+    /*!< zero is returned if this is at the first element or the head. */
+  int items_from_tail(const iterator &where) const;
+    //!< Returns the number of elements between the current item and the tail.
+    /*!< zero is returned if this is at the last element or the tail. */
+
+private:
+  friend class iterator;
+  node *_head;  //!< pointer to the first item.
+  node *_tail;  //!< pointer to the last item.
+
+  bool skip_or_ignore(iterator &where, int count);
+    //!< zips the list to the position indicated by "count", if it can.
+
+  // not permitted.
+  doubly_linked_list(const doubly_linked_list &);
+  doubly_linked_list &operator =(const doubly_linked_list &);
+};
+
+} // namespace.
+
+#endif
+
diff --git a/nucleus/library/nodes/list.cpp b/nucleus/library/nodes/list.cpp
deleted file mode 100644 (file)
index 0ef4dda..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-
-
-
-/*****************************************************************************\
-*                                                                             *
-*  Name   : list                                                              *
-*  Author : Chris Koeritz                                                     *
-*                                                                             *
-*******************************************************************************
-* Copyright (c) 1998-$now By Author.  This program is free software; you can  *
-* redistribute it and/or modify it under the terms of the GNU General Public  *
-* License as published by the Free Software Foundation; either version 2 of   *
-* the License or (at your option) any later version.  This is online at:      *
-*     http://www.fsf.org/copyleft/gpl.html                                    *
-* Please send any updates to: fred@gruntose.com                               *
-\*****************************************************************************/
-
-// POLICIES:
-//
-// the cursor should never be stored to or deleted; it is merely a scanner that
-// runs through the list.
-//
-// the cursor can point at the head or tail.  any storage action is taken to
-// mean that it applies to the closest real object, if one exists.  any query
-// action is taken similarly.
-
-#include "list.h"
-#include "node.h"
-
-#include <basis/functions.h>
-
-namespace nodes {
-
-// nice names for the positions of the next link and the previous link in
-// our node's indices.
-const int PREVIOUS = 0;
-const int NEXT = 1;
-
-//////////////
-
-// iterator functions:
-
-void list::iterator::operator ++()
-{
-  if (is_tail()) return;  // already at the end.
-  _cursor = _cursor->get_link(NEXT);
-}
-
-void list::iterator::operator --()
-{
-  if (is_head()) return;  // already at the front.
-  _cursor = _cursor->get_link(PREVIOUS);
-}
-
-bool list::iterator::operator ==(const iterator &to_compare) const
-{ return _cursor == to_compare._cursor; }
-
-const node *list::iterator::observe()
-{
-  if (!_manager || _manager->empty()) return NULL_POINTER;
-  if (*this == _manager->head()) next();
-  if (*this == _manager->tail()) previous();
-  return _cursor;
-}
-
-node *list::iterator::access()
-{
-  if (!_manager || _manager->empty()) return NULL_POINTER;
-  if (*this == _manager->head()) next();
-  if (*this == _manager->tail()) previous();
-  return _cursor;
-}
-
-bool list::iterator::is_head() const
-{
-  if (!_manager) return false;
-  return _cursor == _manager->_head;
-}
-
-bool list::iterator::is_tail() const
-{ 
-  if (!_manager) return false;
-  return _cursor == _manager->_tail;
-}
-
-void list::iterator::jump_head()
-{
-  if (!_manager) return;
-  _cursor = _manager->_head;
-}
-
-void list::iterator::jump_tail()
-{
-  if (!_manager) return;
-  _cursor = _manager->_tail;
-}
-
-//////////////
-
-list::list()
-: _head(NULL_POINTER), _tail(NULL_POINTER)
-{
-  _head = new node(2);
-  _tail = new node(2);
-  _head->set_link(NEXT, _tail);
-  _tail->set_link(PREVIOUS, _head);
-}
-
-list::~list()
-{
-  iterator zapper = head();
-  while (!empty())
-    zap(zapper);
-  WHACK(_head);
-  WHACK(_tail);
-}
-
-bool list::empty() const
-{
-  if (_head->get_link(NEXT) == _tail) return true;
-  return false;
-}
-
-bool list::set_index(iterator &where, int new_index)
-{
-  if (where._manager != this) return false;
-  if (empty()) return false;
-  node *skipper = _head->get_link(NEXT);
-  for (int i = 0; i < new_index; i++) {
-    skipper = skipper->get_link(NEXT);
-    if (skipper == _tail) return false;  // out of bounds now.
-  }
-  where._cursor = skipper;
-  return true;
-}
-
-bool list::forward(iterator &where, int count)
-{ 
-  if (where._manager != this) return false;
-  if (count <= 0) return true;
-  if (items_from_tail(where) < count) return false;
-  if (where.is_head()) where.next();  // skip the head guard.
-  for (int i = 0; i < count; i++) where.next();
-  return true;
-}
-
-bool list::backward(iterator &where, int count)
-{
-  if (where._manager != this) return false;
-  if (count <= 0) return true;
-  if (items_from_head(where) < count) return false;
-  if (where.is_tail()) where.previous();  // skip the tail guard.
-  for (int i = 0; i < count; i++) where.previous();
-  return true;
-}
-
-int list::elements() const
-{
-  if (empty()) return 0;
-  int to_return = 0;
-  node *skipper = _head->get_link(NEXT);
-  while (skipper != _tail) {
-    to_return++;
-    skipper = skipper->get_link(NEXT);
-  }
-  return to_return;
-}
-
-int list::items_from_head(const iterator &where) const
-{
-  if (where._manager != this) return 0;
-  if (where.is_head()) return 0;  // make sure it's not there already.
-  int index = 0;
-  node *skipper = _head->get_link(NEXT);
-  while ( (where._cursor != skipper) && (skipper != _tail) ) {
-    index++;
-    skipper = skipper->get_link(NEXT);
-  }
-  return index;
-}
-
-int list::items_from_tail(const iterator &where) const
-{
-  if (where._manager != this) return 0;
-  if (where.is_tail()) return 0;  // make sure it's not there already.
-  int index = 0;
-  node *skipper = _tail->get_link(PREVIOUS);
-  while ( (where._cursor != skipper) && (skipper != _head) ) {
-    index++;
-    skipper = skipper->get_link(PREVIOUS);
-  }
-  return index;
-}
-
-/*
-node *list::get()
-{
-  if (empty()) return NULL_POINTER;  // make sure the list isn't empty.
-  if (_cursor == _head) return _head->get_link(NEXT);
-    // check special case for pointing at the head.
-  if (_cursor == _tail) return _tail->get_link(PREVIOUS);
-    // check special case for pointing at the tail.
-  return _cursor;
-}
-*/
-
-node *list::remove(iterator &where)
-{
-  if (where._manager != this) return NULL_POINTER;
-  if (empty()) return NULL_POINTER;
-  if (where._cursor == _head)
-    where._cursor = _head->get_link(NEXT);
-  if (where._cursor == _tail)
-    where._cursor = _tail->get_link(PREVIOUS);
-  node *old_cursor = where._cursor;
-  node *old_previous = old_cursor->get_link(PREVIOUS);
-  node *old_next = old_cursor->get_link(NEXT);
-  old_cursor->set_link(PREVIOUS, NULL_POINTER);
-  old_cursor->set_link(NEXT, NULL_POINTER);
-  old_previous->set_link(NEXT, old_next);
-  old_next->set_link(PREVIOUS, old_previous);
-  where._cursor = old_next;
-  return old_cursor;
-}
-
-void list::zap(iterator &where) { delete remove(where); }
-
-void list::append(iterator &where, node *new_node)
-{
-  if (where._manager != this) return;
-  while (new_node->links() < 2) new_node->insert_link(0, NULL_POINTER);
-  if (empty()) where._cursor = _head;
-  if (where._cursor == _tail)
-    where._cursor = _tail->get_link(PREVIOUS);
-    // shift from the tail sentinel to the tail element.
-  node *save_next = where._cursor->get_link(NEXT);
-  where._cursor->set_link(NEXT, new_node);
-  new_node->set_link(PREVIOUS, where._cursor);
-  new_node->set_link(NEXT, save_next);
-  save_next->set_link(PREVIOUS, new_node);
-  where._cursor = new_node;
-}
-
-void list::insert(iterator &where, node *new_node)
-{
-  if (where._manager != this) return;
-  while (new_node->links() < 2) new_node->insert_link(0, NULL_POINTER);
-  if (empty()) where._cursor = _tail;
-  if (where._cursor == _head)
-    where._cursor = _head->get_link(NEXT);
-    // shift from head sentinel to the head element.
-  node *save_prior = where._cursor->get_link(PREVIOUS);
-  where._cursor->set_link(PREVIOUS, new_node);
-  new_node->set_link(NEXT, where._cursor);
-  new_node->set_link(PREVIOUS, save_prior);
-  save_prior->set_link(NEXT, new_node);
-  where._cursor = new_node;
-}
-
-void list::zap_all()
-{
-  iterator zapper = head();
-  while (!empty()) zap(zapper);
-}
-
-} // namespace.
-
-
-
-
diff --git a/nucleus/library/nodes/list.h b/nucleus/library/nodes/list.h
deleted file mode 100644 (file)
index 5856b49..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-#ifndef LIST_CLASS
-#define LIST_CLASS
-
-/*****************************************************************************\
-*                                                                             *
-*  Name   : list                                                              *
-*  Author : Chris Koeritz                                                     *
-*                                                                             *
-*******************************************************************************
-* Copyright (c) 1998-$now By Author.  This program is free software; you can  *
-* redistribute it and/or modify it under the terms of the GNU General Public  *
-* License as published by the Free Software Foundation; either version 2 of   *
-* the License or (at your option) any later version.  This is online at:      *
-*     http://www.fsf.org/copyleft/gpl.html                                    *
-* Please send any updates to: fred@gruntose.com                               *
-\*****************************************************************************/
-
-
-
-namespace nodes {
-
-class node;  // forward.
-
-//! Implements a guarded, doubly linked list structure.
-/*!
-  The list is viewed one element at a time, through the monocular of an
-  iterator, which keeps track of the current position in the list.  the
-  iterator's cursor can be shifted around at will.  nodes can be added to
-  the list before or after the cursor, and the node pointed at by the cursor
-  can be queried or modified.  Using multiple iterators is fine as long as
-  you guarantee that they either are all just reading the list or that you
-  have a valid access pattern of reads and writes such that no iterator's
-  cursor is affected.  Note that this list is not thread safe.
-*/
-
-class list
-{
-public:
-  list();
-    //!< constructs a blank list.
-
-  ~list();
-    //!< invalidates all contents of the list and destroys all child nodes.
-
-  int elements() const;
-    //!< returns the number of items currently in the list.
-    /*!< this is a costly operation. */
-
-  bool empty() const;
-    //!< returns true if the list is empty.
-    /*!< this is really quick compared to checking the number of elements. */
-
-  //! iterators allow the list to be traversed.
-  /*! NOTE: it is an error to use an iterator on one list with a different
-  list; the methods will do nothing or return empty results in this
-  situation. */
-
-  class iterator {
-  public:
-    iterator(const list *mgr, node *start) : _cursor(start), _manager(mgr) {}
-      //!< opens up an iterator on a list.
-      /*!< the preferred method to construct an iterator is to use the
-      head/tail functions in list. */
-
-    void operator ++();  //!< go to next item.
-    void operator --();  //!< go to previous item.
-    void operator ++(int) { ++(*this); }  //!< post-fix synonym for ++.
-    void operator --(int) { --(*this); }  //!< post-fix synonym for --.
-
-  
-    void next() { (*this)++; }  //!< synonym for ++.
-    void previous() { (*this)--; }  //!< synonyms for --.
-
-    bool operator ==(const iterator &to_compare) const;
-      //!< returns true if the two iterators are at the same position.
-
-    bool is_head() const;
-      //!< returns true if the cursor is at the head of the list.
-      /*!< Note: head() and tail() only return true if the iterator is
-      actually positioned _at_ the head or tail guard.  for example, if the
-      cursor is pointing at the last actual element in the list (but not at
-      the guard itself), then is_tail() returns false.  so, in iteration,
-      your iterator will actually go past the last valid element before
-      is_tail() returns true.  thus it is very important to check is_tail()
-      *before* looking at the node with observe() or access(), since those
-      two will shift the list position to the nearest valid node and away
-      from the guard. */
-    bool is_tail() const;
-      //!< returns true if the cursor is at the tail of the list.
-
-    void jump_head();  //!< set the iterator to the head.
-    void jump_tail();  //!< set the iterator to the tail.
-
-    const node *observe();  //!< peek at the current element.
-      /*!< Note: observe() and access() will return the first element if the
-      list is positioned at the head (or the last if at the tail), and will
-      not return NULL_POINTER for these two positions as long as the list has some
-      elements in it.  the cursor will also have been moved to point at the
-      element that is returned.
-      Another Note: it is extremely important that you do not mess with the
-      links owned by the node (or at least the first two links).
-      A Third Note: these two functions can return NULL_POINTER if the list is empty. */
-    node *access();  //!< obtain access to the current element.
-
-  private:
-    node *_cursor;  //!< the current position.
-    friend class list;  //!< lists have full access to this object.
-    const list *_manager;  //!< our friendly manager.
-  };
-
-  iterator head() const { return iterator(this, _head); }
-    //!< returns an iterator located at the head of the list.
-  iterator tail() const { return iterator(this, _tail); }
-    //!< returns an iterator located at the tail of the list.
-
-  int index(const iterator &it) const { return items_from_head(it); }
-    //!< returns the zero-based index of the cursor's position from the head.
-    /*!< this is a synonym for items_from_head(). */
-
-  bool set_index(iterator &to_set, int new_index);
-    //!< positions the iterator "to_set" at the index specified.
-
-  // storage and retrieval actions.
-  // Note: all of these methods shift the iterator onto the next valid node
-  // if it is positioned at the beginning or end of the list.
-
-  void append(iterator &where, node *new_node);
-    //!< adds a "new_node" into the list immediately after "where".
-    /*!< the nodes previously following the current node (if any) will follow
-    after the "new_node".  this does not change the current position.
-    ownership of "new_node" is taken over by the list. */
-
-  void insert(iterator &where, node *new_node);
-    //!< places a "new_node" immediately before the current node in the list.
-    /*!< the "new_node" will follow the prior precursor to the current node.
-    this does not change the current position.  ownership of "new_node"
-    is taken over by the list.  after the call, the iterator points at
-    the new node. */
-
-  node *remove(iterator &where);
-    //!< extracts the current item from "where" and repairs the hole.
-    /*!< NULL_POINTER is returned if the list was empty.  the current position is set
-    to the node that used to be after the node that has been removed.  after
-    the call, the iterator points at the new node. */
-
-  void zap(iterator &where);
-    //!< wipes out the item at "where", including its contents.
-    /*!< the current position is reset to the item after the now defunct
-    item (or the tail). */
-
-  void zap_all();
-    //!< destroys all the list elements and makes the list empty.
-    /*!< any existing iterators will be invalidated by this. */
-
-  // the following positioning functions could fail if the request is out of
-  // bounds.  for example, forward cannot go beyond the end of the list.  in
-  // such cases, false is returned and the list pointer is not moved.
-
-  bool forward(iterator &where, int count);
-    //!< moves the list pointer "count" items towards the tail.
-    /*!< Note: forward and backward operate on elements; the head and tail
-    guard are not included in the count.  Also, negative values of "count"
-    are ignored. */
-  bool backward(iterator &where, int count);
-    //!< moves the list pointer "count" items towards the head.
-
-  int items_from_head(const iterator &where) const;
-    //!< Returns the number of elements between the current item and the head.
-    /*!< zero is returned if this is at the first element or the head. */
-  int items_from_tail(const iterator &where) const;
-    //!< Returns the number of elements between the current item and the tail.
-    /*!< zero is returned if this is at the last element or the tail. */
-
-private:
-  friend class iterator;
-  node *_head;  //!< pointer to the first item.
-  node *_tail;  //!< pointer to the last item.
-
-  bool skip_or_ignore(iterator &where, int count);
-    //!< zips the list to the position indicated by "count", if it can.
-
-  // not permitted.
-  list(const list &);
-  list &operator =(const list &);
-};
-
-} // namespace.
-
-#endif
-
index b114f9e7b182e9599e0a9fa2b1522920a0650e34..e13af90d6173211091470b4c43d1b17c66d1167f 100644 (file)
@@ -2,7 +2,7 @@ include cpp/variables.def
 
 PROJECT = nodes
 TYPE = library
-SOURCE = list.cpp node.cpp packable_tree.cpp path.cpp symbol_tree.cpp tree.cpp 
+SOURCE = doubly_linked_list.cpp node.cpp packable_tree.cpp path.cpp singly_linked_list.cpp symbol_tree.cpp tree.cpp 
 TARGETS = nodes.lib
 
 include cpp/rules.def
diff --git a/nucleus/library/nodes/singly_linked_list.cpp b/nucleus/library/nodes/singly_linked_list.cpp
new file mode 100644 (file)
index 0000000..97eec6a
--- /dev/null
@@ -0,0 +1,3 @@
+
+#include "singly_linked_list.h"
+
diff --git a/nucleus/library/nodes/singly_linked_list.h b/nucleus/library/nodes/singly_linked_list.h
new file mode 100644 (file)
index 0000000..d58f20f
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef SINGLY_LINKED_LIST_CLASS
+#define SINGLY_LINKED_LIST_CLASS
+
+/*
+*  Name   : singly_linked_list
+*  Author : Chris Koeritz
+**
+* Copyright (c) 1998-$now By Author.  This program is free software; you can
+* redistribute it and/or modify it under the terms of the GNU General Public
+* License as published by the Free Software Foundation; either version 2 of
+* the License or (at your option) any later version.  This is online at:
+*     http://www.fsf.org/copyleft/gpl.html
+* Please send any updates to: fred@gruntose.com
+*/
+#include "node.h"
+
+namespace nodes {
+
+//class node;  // forward.
+
+//! Implements a singly-linked list structure.
+
+class singly_linked_list : public node
+{
+public:
+  singly_linked_list() : node(1) {}
+
+  //hmmm: clean up all children?
+  ~singly_linked_list() {}
+
+  const int NEXT_NODE = 0;  // symbol for the rest of the list linked here.
+
+  int elements() const;
+    //!< returns the number of items currently in the list, including this node.
+    /*!< this is a costly operation. */
+
+  singly_linked_list *next() { return (singly_linked_list *)get_link(NEXT_NODE); }
+
+  void set_next(singly_linked_list *new_next) { set_link(NEXT_NODE, new_next); }
+
+  //! returns true if this list has a cycle in it.
+  static bool has_cycle(singly_linked_list *check) {
+       singly_linked_list *a = check;
+       singly_linked_list *b = check;
+       while ((a != NULL_POINTER) && (b!= NULL_POINTER) ) {
+               a = a->next();  // move position of a forward once.
+         // move position of b forward twice.
+               b = b->next();
+               if (b != NULL_POINTER) b = b->next();
+
+               if (a == b) {
+                       // argh, our single skipper and double skipper have arrived at the same node.
+                       // the only way that can happen is if there's a cycle in the list.
+                       return true;
+               }
+       }
+       // if we fell out of the list iteration with a null for either pointer,
+       // then there was no cycle in the list.
+       return false;
+  }
+
+private:
+  // not permitted.
+  singly_linked_list(const singly_linked_list &);
+  singly_linked_list &operator =(const singly_linked_list &);
+};
+
+} // namespace.
+
+#endif
+
index a8c2e75e94137abf168a08f46e36ea9dd848a76f..19f4b2d6c67ba8aae03e794b5543c10446e58995 100644 (file)
@@ -2,7 +2,7 @@ include cpp/variables.def
 
 PROJECT = tests_node
 TYPE = test
-TARGETS = test_list.exe test_node.exe test_packable_tree.exe test_symbol_tree.exe test_tree.exe
+TARGETS = test_doubly_linked_list.exe test_node.exe test_packable_tree.exe test_singly_linked_list.exe test_symbol_tree.exe test_tree.exe
 LOCAL_LIBS_USED = unit_test application nodes loggers processes filesystem configuration timely textual structures basis 
 DEFINITIONS += USE_FEISTY_MEOW_DLLS
 RUN_TARGETS = $(ACTUAL_TARGETS)
diff --git a/nucleus/library/tests_nodes/test_doubly_linked_list.cpp b/nucleus/library/tests_nodes/test_doubly_linked_list.cpp
new file mode 100644 (file)
index 0000000..be224b8
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+*  Name   : test_doubly_linked_list
+*  Author : Chris Koeritz
+**
+* Copyright (c) 1993-$now By Author.  This program is free software; you can
+* redistribute it and/or modify it under the terms of the GNU General Public
+* License as published by the Free Software Foundation; either version 2 of
+* the License or (at your option) any later version.  This is online at:
+*     http://www.fsf.org/copyleft/gpl.html
+* Please send any updates to: fred@gruntose.com
+*/
+
+#include <application/hoople_main.h>
+#include <basis/astring.h>
+#include <basis/guards.h>
+#include <configuration/application_configuration.h>
+#include <loggers/program_wide_logger.h>
+#include <mathematics/chaos.h>
+#include <nodes/node.h>
+#include <structures/static_memory_gremlin.h>
+#include <unit_test/unit_base.h>
+#include <nodes/doubly_linked_list.h>
+
+using namespace application;
+using namespace basis;
+using namespace configuration;
+using namespace loggers;
+using namespace mathematics;
+using namespace nodes;
+using namespace structures;
+using namespace unit_test;
+
+//#define DEBUG_LIST
+  // uncomment this line to get more debugging output.
+
+const int DEFAULT_ITERATIONS = 50;
+  // the default number of times we run through our phase loop.
+
+typedef basket<int> t_node;
+  // the object we store in the list, a templated integer.
+
+#define CASTER(bare_node) static_cast<const t_node *>(bare_node)
+  // turns a node pointer into our special t_node.
+
+#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print)
+
+//////////////
+
+class test_doubly_linked_list : virtual public unit_base, virtual public application_shell
+{
+public:
+  test_doubly_linked_list() : unit_base() {}
+  DEFINE_CLASS_NAME("test_list");
+  virtual int execute();
+};
+
+HOOPLE_MAIN(test_doubly_linked_list, );
+
+//////////////
+
+int test_doubly_linked_list::execute()
+{
+  FUNCDEF("execute");
+
+  doubly_linked_list the_list;
+  chaos randomizer;
+
+  int iterations_left = DEFAULT_ITERATIONS;
+  while (iterations_left-- > 0) {
+
+    // run through the phases below as many times as we are told.
+
+    {
+      // phase for adding a random number into the list.
+      int to_add = randomizer.inclusive(0, 100000);
+
+      // seek the correct insertion place to keep the list ordered.
+      doubly_linked_list::iterator iter = the_list.head();
+      while (!iter.is_tail() && iter.observe()
+          && (CASTER(iter.observe())->stored() <= to_add) )
+        iter++;
+      the_list.insert(iter, new t_node(2, to_add));
+    }
+
+    {
+      // test the list invariant (which is that all elements should be sorted
+      // in non-decreasing order).
+      doubly_linked_list::iterator iter = the_list.tail();
+      // initialize our comparator.
+      int bigger = CASTER(iter.observe())->stored();
+      // loop backwards until we hit the head.
+      while (!iter.is_head()) {
+        // check that the last value is not less than the current value.
+        ASSERT_FALSE(bigger < CASTER(iter.observe())->stored(),
+            "invariant check should not find a mal-ordering in the list");
+        bigger = CASTER(iter.observe())->stored();
+        iter--;
+      }
+    }
+
+    {
+      // if the conditions are favorable, we whack at least one element out of
+      // the list.
+      if (randomizer.inclusive(1, 100) < 20) {
+        int elem = the_list.elements();
+        int to_whack = randomizer.inclusive(0, elem - 1);
+        
+        // start at the head of the list...
+        doubly_linked_list::iterator iter = the_list.head();
+        // and jump to the element we chose.
+        the_list.forward(iter, to_whack);
+        ASSERT_EQUAL(the_list.index(iter), to_whack,
+            "forward should not see logic error where index of element to zap is incorrect");
+        ASSERT_FALSE(iter.is_tail(),
+            "forward should not see logic error where we get to the tail somehow");
+        the_list.zap(iter);
+      }
+    }
+
+  }
+
+#ifdef DEBUG_LIST
+  doubly_linked_list::iterator iter = the_list.head();
+  log(astring(""));
+  log(astring("list contents:"));
+  int indy = 0;
+  while (!iter.is_tail()) {
+    int item = CASTER(iter.observe())->stored();
+    log(a_sprintf("item #%d: %d", indy, item));
+    indy++;
+    iter++;
+  }
+#endif
+
+  return final_report();
+}
+
+
diff --git a/nucleus/library/tests_nodes/test_list.cpp b/nucleus/library/tests_nodes/test_list.cpp
deleted file mode 100644 (file)
index 14a4fc7..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*****************************************************************************\
-*                                                                             *
-*  Name   : test_list                                                         *
-*  Author : Chris Koeritz                                                     *
-*                                                                             *
-*******************************************************************************
-* Copyright (c) 1993-$now By Author.  This program is free software; you can  *
-* redistribute it and/or modify it under the terms of the GNU General Public  *
-* License as published by the Free Software Foundation; either version 2 of   *
-* the License or (at your option) any later version.  This is online at:      *
-*     http://www.fsf.org/copyleft/gpl.html                                    *
-* Please send any updates to: fred@gruntose.com                               *
-\*****************************************************************************/
-
-#include <application/hoople_main.h>
-#include <basis/astring.h>
-#include <basis/guards.h>
-#include <configuration/application_configuration.h>
-#include <loggers/program_wide_logger.h>
-#include <mathematics/chaos.h>
-#include <nodes/list.h>
-#include <nodes/node.h>
-#include <structures/static_memory_gremlin.h>
-#include <unit_test/unit_base.h>
-
-using namespace application;
-using namespace basis;
-using namespace configuration;
-using namespace loggers;
-using namespace mathematics;
-using namespace nodes;
-using namespace structures;
-using namespace unit_test;
-
-//#define DEBUG_LIST
-  // uncomment this line to get more debugging output.
-
-const int DEFAULT_ITERATIONS = 50;
-  // the default number of times we run through our phase loop.
-
-typedef basket<int> t_node;
-  // the object we store in the list, a templated integer.
-
-#define CASTER(bare_node) static_cast<const t_node *>(bare_node)
-  // turns a node pointer into our special t_node.
-
-#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print)
-
-//////////////
-
-class test_list : virtual public unit_base, virtual public application_shell
-{
-public:
-  test_list() : unit_base() {}
-  DEFINE_CLASS_NAME("test_list");
-  virtual int execute();
-};
-
-HOOPLE_MAIN(test_list, );
-
-//////////////
-
-int test_list::execute()
-{
-  FUNCDEF("execute");
-
-  list the_list;
-  chaos randomizer;
-
-  int iterations_left = DEFAULT_ITERATIONS;
-  while (iterations_left-- > 0) {
-
-    // run through the phases below as many times as we are told.
-
-    {
-      // phase for adding a random number into the list.
-      int to_add = randomizer.inclusive(0, 100000);
-
-      // seek the correct insertion place to keep the list ordered.
-      list::iterator iter = the_list.head();
-      while (!iter.is_tail() && iter.observe()
-          && (CASTER(iter.observe())->stored() <= to_add) )
-        iter++;
-      the_list.insert(iter, new t_node(2, to_add));
-    }
-
-    {
-      // test the list invariant (which is that all elements should be sorted
-      // in non-decreasing order).
-      list::iterator iter = the_list.tail();
-      // initialize our comparator.
-      int bigger = CASTER(iter.observe())->stored();
-      // loop backwards until we hit the head.
-      while (!iter.is_head()) {
-        // check that the last value is not less than the current value.
-        ASSERT_FALSE(bigger < CASTER(iter.observe())->stored(),
-            "invariant check should not find a mal-ordering in the list");
-        bigger = CASTER(iter.observe())->stored();
-        iter--;
-      }
-    }
-
-    {
-      // if the conditions are favorable, we whack at least one element out of
-      // the list.
-      if (randomizer.inclusive(1, 100) < 20) {
-        int elem = the_list.elements();
-        int to_whack = randomizer.inclusive(0, elem - 1);
-        
-        // start at the head of the list...
-        list::iterator iter = the_list.head();
-        // and jump to the element we chose.
-        the_list.forward(iter, to_whack);
-        ASSERT_EQUAL(the_list.index(iter), to_whack,
-            "forward should not see logic error where index of element to zap is incorrect");
-        ASSERT_FALSE(iter.is_tail(),
-            "forward should not see logic error where we get to the tail somehow");
-        the_list.zap(iter);
-      }
-    }
-
-  }
-
-#ifdef DEBUG_LIST
-  list::iterator iter = the_list.head();
-  log(astring(""));
-  log(astring("list contents:"));
-  int indy = 0;
-  while (!iter.is_tail()) {
-    int item = CASTER(iter.observe())->stored();
-    log(a_sprintf("item #%d: %d", indy, item));
-    indy++;
-    iter++;
-  }
-#endif
-
-  return final_report();
-}
-
-
diff --git a/nucleus/library/tests_nodes/test_singly_linked_list.cpp b/nucleus/library/tests_nodes/test_singly_linked_list.cpp
new file mode 100644 (file)
index 0000000..e5e88c5
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+*  Name   : test_singly_linked_list
+*  Author : Chris Koeritz
+**
+* Copyright (c) 1993-$now By Author.  This program is free software; you can
+* redistribute it and/or modify it under the terms of the GNU General Public
+* License as published by the Free Software Foundation; either version 2 of
+* the License or (at your option) any later version.  This is online at:
+*     http://www.fsf.org/copyleft/gpl.html
+* Please send any updates to: fred@gruntose.com
+*/
+
+#include <application/hoople_main.h>
+#include <basis/astring.h>
+#include <basis/guards.h>
+#include <configuration/application_configuration.h>
+#include <loggers/program_wide_logger.h>
+#include <mathematics/chaos.h>
+#include <nodes/singly_linked_list.h>
+#include <structures/static_memory_gremlin.h>
+#include <unit_test/unit_base.h>
+
+using namespace application;
+using namespace basis;
+using namespace configuration;
+using namespace loggers;
+using namespace mathematics;
+using namespace nodes;
+using namespace structures;
+using namespace unit_test;
+
+//#define DEBUG_LIST
+  // uncomment this line to get more debugging output.
+
+const int DEFAULT_ITERATIONS = 50;
+  // the default number of times we run through our phase loop.
+
+typedef basket<int> t_node;
+  // the object we store in the list, a templated integer.
+
+#define CASTER(bare_node) static_cast<const t_node *>(bare_node)
+  // turns a node pointer into our special t_node.
+
+#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print)
+
+//////////////
+
+class test_singly_linked_list : virtual public unit_base, virtual public application_shell
+{
+public:
+  test_singly_linked_list() : unit_base() {}
+  DEFINE_CLASS_NAME("test_list");
+  virtual int execute();
+};
+
+HOOPLE_MAIN(test_singly_linked_list, );
+
+//////////////
+
+int test_singly_linked_list::execute()
+{
+  FUNCDEF("execute");
+
+  // first some discrete tests, before we start looping around.
+
+  {
+       // test that the cycle detector is working for finding a cycle.
+       singly_linked_list *a = new singly_linked_list();
+       singly_linked_list *b = new singly_linked_list();
+       singly_linked_list *c = new singly_linked_list();
+       singly_linked_list *d = new singly_linked_list();
+       singly_linked_list *e = new singly_linked_list();
+       a->set_next(b);
+       b->set_next(c);
+       c->set_next(d);
+       d->set_next(e);
+       e->set_next(c);
+
+       ASSERT_TRUE(singly_linked_list::has_cycle(a), "list should be found to have a cycle")
+  }
+
+  {
+       // test that the cycle detector is working to not find a cycle if one doesn't exist.
+       singly_linked_list *a = new singly_linked_list();
+       singly_linked_list *b = new singly_linked_list();
+       singly_linked_list *c = new singly_linked_list();
+       singly_linked_list *d = new singly_linked_list();
+       singly_linked_list *e = new singly_linked_list();
+       a->set_next(b);
+       b->set_next(c);
+       c->set_next(d);
+       d->set_next(e);
+//     e->set_next(NULL_POINTER);  // not actually necessary, right?
+
+       ASSERT_FALSE(singly_linked_list::has_cycle(a), "list should be found to have zero cycles")
+  }
+
+  singly_linked_list *the_list = new singly_linked_list();
+
+  chaos randomizer;
+
+  int iterations_left = DEFAULT_ITERATIONS;
+  while (iterations_left-- > 0) {
+
+    // run through the phases below as many times as we are told.
+
+    {
+      // phase for adding a random number into the list.
+      int to_add = randomizer.inclusive(0, 100000);
+
+//      // seek the correct insertion place to keep the list ordered.
+//      singly_linked_list::iterator iter = the_list.head();
+//      while (!iter.is_tail() && iter.observe()
+//          && (CASTER(iter.observe())->stored() <= to_add) )
+//        iter++;
+//      the_list.insert(iter, new t_node(2, to_add));
+    }
+
+    {
+//      // test the list invariant (which is that all elements should be sorted
+//      // in non-decreasing order).
+//      singly_linked_list::iterator iter = the_list.tail();
+//      // initialize our comparator.
+//      int bigger = CASTER(iter.observe())->stored();
+//      // loop backwards until we hit the head.
+//      while (!iter.is_head()) {
+//        // check that the last value is not less than the current value.
+//        ASSERT_FALSE(bigger < CASTER(iter.observe())->stored(),
+//            "invariant check should not find a mal-ordering in the list");
+//        bigger = CASTER(iter.observe())->stored();
+//        iter--;
+//      }
+    }
+
+    {
+//      // if the conditions are favorable, we whack at least one element out of
+//      // the list.
+//      if (randomizer.inclusive(1, 100) < 20) {
+//        int elem = the_list.elements();
+//        int to_whack = randomizer.inclusive(0, elem - 1);
+//
+//        // start at the head of the list...
+//        singly_linked_list::iterator iter = the_list.head();
+//        // and jump to the element we chose.
+//        the_list.forward(iter, to_whack);
+//        ASSERT_EQUAL(the_list.index(iter), to_whack,
+//            "forward should not see logic error where index of element to zap is incorrect");
+//        ASSERT_FALSE(iter.is_tail(),
+//            "forward should not see logic error where we get to the tail somehow");
+//        the_list.zap(iter);
+//      }
+    }
+
+  }
+
+//#ifdef DEBUG_LIST
+//  singly_linked_list::iterator iter = the_list.head();
+//  log(astring(""));
+//  log(astring("list contents:"));
+//  int indy = 0;
+//  while (!iter.is_tail()) {
+//    int item = CASTER(iter.observe())->stored();
+//    log(a_sprintf("item #%d: %d", indy, item));
+//    indy++;
+//    iter++;
+//  }
+//#endif
+
+  return final_report();
+}
+
+