Apply patches #5828, #5837, #5841, #5843.
authorBen Pfaff <blp@gnu.org>
Tue, 3 Apr 2007 22:55:07 +0000 (22:55 +0000)
committerBen Pfaff <blp@gnu.org>
Tue, 3 Apr 2007 22:55:07 +0000 (22:55 +0000)
In tests:

* automake.mk (tests_libpspp_bt_test_LDADD): Add range-map-test,
range-set-test, tower-test.

* libpspp/range-map-test.c: New test.

* libpspp/range-set-test.c: New test.

* libpspp/tower-test.c: New test.

In src/libpspp:

* abt.c (insert_relative): New function.
(abt_insert_after): New function.
(abt_insert_before): New function.

* range-map.c: New file.

* range-map.h: New file.

* range-set.c: New file.

* range-set.h: New file.

* tower.c: New file.

* tower.h: New file.

16 files changed:
src/libpspp/ChangeLog
src/libpspp/abt.c
src/libpspp/abt.h
src/libpspp/automake.mk
src/libpspp/range-map.c [new file with mode: 0644]
src/libpspp/range-map.h [new file with mode: 0644]
src/libpspp/range-set.c [new file with mode: 0644]
src/libpspp/range-set.h [new file with mode: 0644]
src/libpspp/tower.c [new file with mode: 0644]
src/libpspp/tower.h [new file with mode: 0644]
tests/ChangeLog
tests/automake.mk
tests/libpspp/abt-test.c
tests/libpspp/range-map-test.c [new file with mode: 0644]
tests/libpspp/range-set-test.c [new file with mode: 0644]
tests/libpspp/tower-test.c [new file with mode: 0644]

index b4575a528d570493cfda5b97bdc9c9c3489e56a1..ca06aea9b05f5641204719c3f657a406152b80d7 100644 (file)
@@ -1,3 +1,23 @@
+2007-04-03  Ben Pfaff  <blp@gnu.org>
+
+       Apply patches #5828, #5837, #5841, #5843.
+
+       * abt.c (insert_relative): New function.
+       (abt_insert_after): New function.
+       (abt_insert_before): New function.
+
+       * range-map.c: New file.
+
+       * range-map.h: New file.
+
+       * range-set.c: New file.
+
+       * range-set.h: New file.
+
+       * tower.c: New file.
+
+       * tower.h: New file.
+
 2007-04-01  Ben Pfaff  <blp@gnu.org>
 
        * bt.c: Need #include <limits.h>.  Thanks to "John McCabe-Dansted"
index c181b90473f84b19590c572de6d9961aa40115c4..29604b3442d2fbbc4ea4e5ebb85c94887b2f2168 100644 (file)
 
 #include <libpspp/abt.h>
 
+#include <stdbool.h>
+
+#include <libpspp/assertion.h>
+
 static struct abt_node **down_link (struct abt *, struct abt_node *);
 static struct abt_node *skew (struct abt *, struct abt_node *);
 static struct abt_node *split (struct abt *, struct abt_node *);
 
 /* Initializes ABT as an empty ABT that uses COMPARE and
-   REAUGMENT functions, passing in AUX as auxiliary data. */
+   REAUGMENT functions, passing in AUX as auxiliary data.
+
+   The comparison function is optional.  If it is null, this
+   indicates that the tree is being used for its augmentations
+   only.  ABT functions that compare nodes may not be used with
+   trees that lack comparison functions; contrariwise, other
+   functions that could disrupt the ordering of a tree may not be
+   used if a comparison function is specified.  Refer to
+   individual function descriptions for details. */
 void
 abt_init (struct abt *abt,
           abt_compare_func *compare,
           abt_reaugment_func *reaugment,
           const void *aux) 
 {
+  assert (reaugment != NULL);
   abt->root = NULL;
   abt->compare = compare;
   abt->reaugment = reaugment;
@@ -53,7 +66,9 @@ abt_init (struct abt *abt,
 /* Inserts the given NODE into ABT.
    Returns a null pointer if successful.
    Returns the existing node already in ABT equal to NODE, on
-   failure. */
+   failure.
+   This function may be used only if ABT has a comparison
+   function. */
 struct abt_node *
 abt_insert (struct abt *abt, struct abt_node *node) 
 {
@@ -99,6 +114,77 @@ abt_insert (struct abt *abt, struct abt_node *node)
   return NULL;
 }
 
+/* Inserts NODE before or after node P in ABT, depending on the
+   value of AFTER.
+   If P is null, then the node is inserted as the first node in
+   the tree, if AFTER is true, or the last node, if AFTER is
+   false. */
+static inline void
+insert_relative (struct abt *abt, struct abt_node *p, bool after,
+                 struct abt_node *node)
+{
+  node->down[0] = NULL;
+  node->down[1] = NULL;
+  node->level = 1;
+
+  if (abt->root == NULL) 
+    {
+      assert (p == NULL);
+      abt->root = node;
+      node->up = NULL;
+      abt_reaugmented (abt, node);
+    }
+  else 
+    {
+      int dir = after;
+      if (p == NULL) 
+        {
+          p = abt->root;
+          dir = !after;
+        }
+      while (p->down[dir] != NULL) 
+        {
+          p = p->down[dir];
+          dir = !after; 
+        }
+      p->down[dir] = node;
+      node->up = p;
+      abt_reaugmented (abt, node);
+    }
+
+  while ((node = node->up) != NULL) 
+    {
+      node = skew (abt, node);
+      node = split (abt, node);
+    }
+}
+
+/* Inserts NODE after node P in ABT.
+   If P is null, then the node is inserted as the first node in
+   the tree.
+   This function may be used only if ABT lacks a comparison
+   function. */
+void
+abt_insert_after (struct abt *abt,
+                  const struct abt_node *p, struct abt_node *node) 
+{
+  assert (abt->compare == NULL);
+  insert_relative (abt, (struct abt_node *) p, true, node);
+}
+
+/* Inserts NODE before node P in ABT.
+   If P is null, then the node is inserted as the last node in
+   the tree.
+   This function may be used only if ABT lacks a comparison
+   function. */
+void
+abt_insert_before (struct abt *abt,
+                   const struct abt_node *p, struct abt_node *node) 
+{
+  assert (abt->compare == NULL);
+  insert_relative (abt, (struct abt_node *) p, false, node);
+}
+
 /* Deletes P from ABT. */
 void
 abt_delete (struct abt *abt, struct abt_node *p)
@@ -183,7 +269,9 @@ abt_last (const struct abt *abt)
 }
 
 /* Searches ABT for a node equal to TARGET.
-   Returns the node if found, or a null pointer otherwise. */
+   Returns the node if found, or a null pointer otherwise.
+   This function may be used only if ABT has a comparison
+   function. */
 struct abt_node *
 abt_find (const struct abt *abt, const struct abt_node *target)
 {
@@ -282,7 +370,11 @@ abt_reaugmented (const struct abt *abt, struct abt_node *p)
    call this function for each node.  Instead, update a single
    node's key, call this function, update another node's key, and
    so on.  Alternatively, remove all affected nodes from the
-   tree, update their keys, then re-insert all of them. */
+   tree, update their keys, then re-insert all of them.
+
+   This function may be used only if ABT has a comparison
+   function.  If it doesn't, then you probably just want
+   abt_reaugmented. */
 struct abt_node *
 abt_changed (struct abt *abt, struct abt_node *p) 
 {
@@ -312,7 +404,10 @@ abt_changed (struct abt *abt, struct abt_node *p)
    function for each node.  Instead, move a single node, call
    this function, move another node, and so on.  Alternatively,
    remove all affected nodes from the tree, move them, then
-   re-insert all of them. */
+   re-insert all of them.
+
+   This function may be used only if ABT has a comparison
+   function. */
 void
 abt_moved (struct abt *abt, struct abt_node *p) 
 {
index f48e7a2ab8893c33e995d3f7f69ef965c3689a71..e64ad289304d009d8119b38b162a46b2db66eed4 100644 (file)
@@ -61,7 +61,7 @@
    larger tree is rearranged, e.g. via a series of rotations,
    then the implementation will not call the reaugmentation
    function outside of the subtree, because the overall
-   augmentation data for the subtree is assumed not to changed.
+   augmentation data for the subtree is assumed not to change.
    This optimization is valid for the forms of augmentation
    described in CLR and Knuth (see below), and it is possible
    that it is valid for every efficient binary search tree
@@ -193,6 +193,10 @@ void abt_init (struct abt *, abt_compare_func *, abt_reaugment_func *,
                const void *aux);
 
 struct abt_node *abt_insert (struct abt *, struct abt_node *);
+void abt_insert_after (struct abt *,
+                       const struct abt_node *, struct abt_node *);
+void abt_insert_before (struct abt *,
+                        const struct abt_node *, struct abt_node *);
 void abt_delete (struct abt *, struct abt_node *);
 
 struct abt_node *abt_first (const struct abt *);
index 1af66697354f737fc6f5e8abb7c046bc3f9a677a..265178d4f417298053151932e33d3507caa2e2d9 100644 (file)
@@ -47,6 +47,10 @@ src_libpspp_libpspp_a_SOURCES = \
        src/libpspp/misc.h \
        src/libpspp/pool.c \
        src/libpspp/pool.h \
+       src/libpspp/range-map.c \
+       src/libpspp/range-map.h \
+       src/libpspp/range-set.c \
+       src/libpspp/range-set.h \
        src/libpspp/sparse-array.c \
        src/libpspp/sparse-array.h \
        src/libpspp/syntax-gen.c \
@@ -57,6 +61,8 @@ src_libpspp_libpspp_a_SOURCES = \
        src/libpspp/start-date.h \
        src/libpspp/str.c \
        src/libpspp/str.h \
+       src/libpspp/tower.c \
+       src/libpspp/tower.h \
        src/libpspp/verbose-msg.c \
        src/libpspp/verbose-msg.h \
        src/libpspp/version.h 
diff --git a/src/libpspp/range-map.c b/src/libpspp/range-map.c
new file mode 100644 (file)
index 0000000..77e7718
--- /dev/null
@@ -0,0 +1,158 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+
+#include <libpspp/range-map.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/compiler.h>
+
+static struct range_map_node *bt_to_range_map_node (const struct bt_node *);
+static int compare_range_map_nodes (const struct bt_node *,
+                                    const struct bt_node *,
+                                    const void *aux);
+static struct range_map_node *first_node (const struct range_map *);
+static struct range_map_node *next_node (const struct range_map *,
+                                         const struct range_map_node *);
+static struct range_map_node *prev_node (const struct range_map *,
+                                         const struct range_map_node *) UNUSED;
+
+/* Initializes RM as an empty range map. */
+void
+range_map_init (struct range_map *rm) 
+{
+  bt_init (&rm->bt, compare_range_map_nodes, NULL);
+}
+
+/* Returns true if RM contains no mappings,
+   false if it contains at least one. */
+bool
+range_map_is_empty (const struct range_map *rm) 
+{
+  return bt_count (&rm->bt) == 0;
+}
+
+/* Inserts node NEW into RM, covering the range beginning at
+   START and ending at START + WIDTH (exclusive).  WIDTH must be
+   at least 1.  The new range must not overlap any existing range
+   already in RM. */
+void
+range_map_insert (struct range_map *rm,
+                  unsigned long int start, unsigned long int width,
+                  struct range_map_node *new)
+{
+  unsigned long int end = start + width;
+  struct range_map_node *dup;
+
+  assert (width > 0);
+  assert (end - 1 >= start);
+  
+  new->start = start;
+  new->end = end;
+  dup = bt_to_range_map_node (bt_insert (&rm->bt, &new->bt_node));
+
+  /* Make sure NEW doesn't overlap any other node. */
+  assert (dup == NULL);
+  assert (prev_node (rm, new) == NULL || start >= prev_node (rm, new)->end);
+  assert (next_node (rm, new) == NULL || next_node (rm, new)->start >= end);
+}
+
+/* Deletes NODE from RM. */
+void
+range_map_delete (struct range_map *rm, struct range_map_node *node) 
+{
+  bt_delete (&rm->bt, &node->bt_node);
+}
+
+/* Returns the node in RM that contains the given POSITION, or a
+   null pointer if no node contains POSITION. */
+struct range_map_node *
+range_map_lookup (const struct range_map *rm,
+                  unsigned long int position) 
+{
+  struct range_map_node tmp, *node;
+
+  tmp.start = position;
+  node = bt_to_range_map_node (bt_find_le (&rm->bt, &tmp.bt_node));
+  return node != NULL && position < node->end ? node : NULL;
+}
+
+/* Returns the first node in RM, or a null pointer if RM is
+   empty. */
+struct range_map_node *
+range_map_first (const struct range_map *rm) 
+{
+  return first_node (rm);
+}
+
+/* If NODE is nonnull, returns the node in RM following NODE, or
+   a null pointer if NODE is the last node in RM.
+   If NODE is null, behaves like range_map_first. */
+struct range_map_node *
+range_map_next (const struct range_map *rm,
+                const struct range_map_node *node) 
+{
+  return node != NULL ? next_node (rm, node) : first_node (rm);
+}
+\f
+/* Returns the range_map_node containing BT_NODE. */
+static struct range_map_node *
+bt_to_range_map_node (const struct bt_node *bt_node) 
+{
+  return (bt_node != NULL
+          ? bt_data (bt_node, struct range_map_node, bt_node)
+          : NULL);
+}
+
+/* Compares range map nodes A and B and returns a strcmp()-type
+   result. */
+static int
+compare_range_map_nodes (const struct bt_node *a_,
+                         const struct bt_node *b_,
+                         const void *aux UNUSED) 
+{
+  const struct range_map_node *a = bt_to_range_map_node (a_);
+  const struct range_map_node *b = bt_to_range_map_node (b_);
+  return a->start < b->start ? -1 : a->start > b->start;
+}
+
+/* Returns the first range map node in RM, or a null pointer if
+   RM is empty. */
+static struct range_map_node *
+first_node (const struct range_map *rm) 
+{
+  return bt_to_range_map_node (bt_first (&rm->bt));
+}
+
+/* Returns the next range map node in RM following NODE, or a
+   null pointer if NODE is the last node in RM. */
+static struct range_map_node *
+next_node (const struct range_map *rm, const struct range_map_node *node) 
+{
+  return bt_to_range_map_node (bt_next (&rm->bt, &node->bt_node));
+}
+
+/* Returns the previous range map node in RM preceding NODE, or a
+   null pointer if NODE is the first node in RM. */
+static struct range_map_node *
+prev_node (const struct range_map *rm, const struct range_map_node *node) 
+{
+  return bt_to_range_map_node (bt_prev (&rm->bt, &node->bt_node));
+}
+
diff --git a/src/libpspp/range-map.h b/src/libpspp/range-map.h
new file mode 100644 (file)
index 0000000..8a5f6f4
--- /dev/null
@@ -0,0 +1,95 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef LIBPSPP_RANGE_MAP_H
+#define LIBPSPP_RANGE_MAP_H
+
+/* Range map data structure, implemented as a balanced binary
+   tree.
+
+   This is a dictionary data structure that maps from contiguous
+   ranges of "unsigned long int" keys to arbitrary data
+   values.
+
+   The implementation is not robust against ranges that include
+   ULONG_MAX in their ranges.  Such ranges are difficult to deal
+   with in C anyhow, because a range that includes 0 through
+   ULONG_MAX inclusive has a width of ULONG_MAX + 1, which equals
+   0. */
+
+#include <stdbool.h>
+
+#include <libpspp/bt.h>
+
+/* Returns the data structure corresponding to the given NODE,
+   assuming that NODE is embedded as the given MEMBER name in
+   data type STRUCT. */
+#define range_map_data(NODE, STRUCT, MEMBER)                            \
+        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+
+/* A range map node, to be embedded in the data value. */
+struct range_map_node 
+  {
+    struct bt_node bt_node;     /* Balanced tree node. */
+    unsigned long int start;    /* Start of range. */
+    unsigned long int end;      /* End of range, plus one. */
+  };
+
+/* Returns the start of the range in the given NODE. */
+static inline unsigned long int
+range_map_node_get_start (const struct range_map_node *node) 
+{
+  return node->start;
+}
+
+/* Returns the end of the range in the given NODE, plus one. */
+static inline unsigned long int
+range_map_node_get_end (const struct range_map_node *node) 
+{
+  return node->end;
+}
+
+/* Returns the width of the range in the given NODE. */
+static inline unsigned long int
+range_map_node_get_width (const struct range_map_node *node) 
+{
+  return node->end - node->start;
+}
+\f
+/* Range map. */
+struct range_map 
+  {
+    struct bt bt;
+  };
+
+void range_map_init (struct range_map *);
+
+bool range_map_is_empty (const struct range_map *);
+
+void range_map_insert (struct range_map *,
+                       unsigned long int start, unsigned long int width,
+                       struct range_map_node *new);
+void range_map_delete (struct range_map *, struct range_map_node *);
+
+struct range_map_node *range_map_lookup (const struct range_map *,
+                                         unsigned long int position);
+struct range_map_node *range_map_first (const struct range_map *);
+struct range_map_node *range_map_next (const struct range_map *,
+                                       const struct range_map_node *);
+
+#endif /* libpspp/range-map.h */
diff --git a/src/libpspp/range-set.c b/src/libpspp/range-set.c
new file mode 100644 (file)
index 0000000..4e7d6d2
--- /dev/null
@@ -0,0 +1,492 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+/* Bitmap, implemented as a balanced binary tree. */
+
+/* If you add routines in this file, please add a corresponding
+   test to range-set-test.c.  This test program should achieve
+   100% coverage of lines and branches in this code, as reported
+   by "gcov -b". */
+
+#include <config.h>
+
+#include <libpspp/range-set.h>
+
+#include <limits.h>
+#include <stdlib.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/bt.h>
+#include <libpspp/compiler.h>
+#include <libpspp/pool.h>
+
+#include "xalloc.h"
+
+/* A set of ranges. */
+struct range_set 
+  {
+    struct pool *pool;                  /* Pool for freeing range_set. */
+    struct bt bt;                       /* Tree of range_set_nodes. */
+
+    /* Cache. */
+    unsigned long int cache_start;      /* Start of region. */
+    unsigned long int cache_end;        /* One past end of region. */
+    bool cache_value;                   /* Is the region in the set? */
+  };
+
+/* A node in the range set. */
+struct range_set_node 
+  {
+    struct bt_node bt_node;             /* Binary tree node. */
+    unsigned long int start;            /* Start of region. */
+    unsigned long int end;              /* One past end of region. */
+  };
+
+static struct range_set_node *bt_to_rs_node (const struct bt_node *); 
+static int compare_range_set_nodes (const struct bt_node *,
+                                    const struct bt_node *,
+                                    const void *aux);
+static struct range_set_node *first_node (const struct range_set *);
+static struct range_set_node *next_node (const struct range_set *,
+                                         const struct range_set_node *);
+static void delete_node (struct range_set *, struct range_set_node *);
+static struct range_set_node *delete_node_get_next (struct range_set *,
+                                                    struct range_set_node *);
+static struct range_set_node *find_node_le (const struct range_set *,
+                                            unsigned long int position);
+static struct range_set_node *insert_node (struct range_set *,
+                                           unsigned long int start,
+                                           unsigned long int end);
+static void insert_just_before (struct range_set *,
+                                unsigned long int start, unsigned long int end,
+                                struct range_set_node *);
+static void merge_node_with_successors (struct range_set *,
+                                        struct range_set_node *);
+static void destroy_pool (void *);
+
+/* Creates and returns a new, empty range set. */
+struct range_set *
+range_set_create (void) 
+{
+  return range_set_create_pool (NULL);
+}
+
+/* Creates and returns a new, empty range set in the given
+   POOL. */
+struct range_set *
+range_set_create_pool (struct pool *pool) 
+{
+  struct range_set *rs = xmalloc (sizeof *rs);
+  rs->pool = pool;
+  if (pool != NULL)
+    pool_register (pool, destroy_pool, rs);
+  bt_init (&rs->bt, compare_range_set_nodes, NULL);
+  rs->cache_end = 0;
+  return rs;
+}
+
+/* Destroys range set RS. */
+void
+range_set_destroy (struct range_set *rs) 
+{
+  if (rs != NULL) 
+    {
+      if (rs->pool != NULL)
+        pool_unregister (rs->pool, rs);
+      while (!range_set_is_empty (rs)) 
+        delete_node (rs, first_node (rs));
+      free (rs); 
+    }
+}
+
+/* Inserts the region starting at START and extending for WIDTH
+   into RS. */
+void
+range_set_insert (struct range_set *rs,
+                  unsigned long int start, unsigned long int width) 
+{
+  unsigned long int end = start + width;
+  struct range_set_node *node;
+
+  assert (width == 0 || start + width - 1 >= start);
+
+  if (width == 0)
+    return;
+
+  /* Invalidate cache. */
+  rs->cache_end = 0;
+
+  node = find_node_le (rs, start);
+  if (node != NULL) 
+    {
+      if (start > node->end) 
+        {
+          /* New region does not overlap NODE, but it might
+             overlap the next node. */
+          insert_just_before (rs, start, end, next_node (rs, node));
+        }
+      else if (end > node->end) 
+        {
+          /* New region starts in the middle of NODE and
+             continues past its end, so extend NODE, then merge
+             it with any following node that it now potentially
+             overlaps. */
+          node->end = end;
+          merge_node_with_successors (rs, node); 
+        }
+      else 
+        {
+          /* New region is completely contained by NODE, so
+             there's nothing to do. */
+        }
+    }
+  else 
+    {
+      /* New region starts before any existing region, but it
+         might overlap the first region. */
+      insert_just_before (rs, start, end, first_node (rs)); 
+    }
+}
+
+/* Inserts the region starting at START and extending for WIDTH
+   from RS. */
+void
+range_set_delete (struct range_set *rs,
+                  unsigned long int start, unsigned long int width) 
+{
+  unsigned long int end = start + width;
+  struct range_set_node *node;
+
+  assert (width == 0 || start + width - 1 >= start);
+
+  if (width == 0)
+    return;
+
+  /* Invalidate cache. */
+  rs->cache_end = 0;
+
+  node = find_node_le (rs, start);
+  if (node == NULL) 
+    node = first_node (rs);
+  
+  while (node != NULL && end > node->start) 
+    {
+      if (start <= node->start) 
+        {
+          if (end >= node->end)
+            {
+              /* Region to delete covers entire node. */
+              node = delete_node_get_next (rs, node);
+            }
+          else
+            {
+              /* Region to delete covers only left part of node. */
+              node->start = end;
+              break;
+            }
+        }
+      else if (start < node->end)
+        {
+          if (end < node->end) 
+            {
+              /* Region to delete covers middle of the node, so
+                 we have to split the node into two pieces. */
+              unsigned long int old_node_end = node->end;
+              node->end = start;
+              insert_node (rs, end, old_node_end);
+              break;
+            }
+          else 
+            {
+              /* Region to delete covers only right part of
+                 node. */
+              node->end = start;
+              node = next_node (rs, node);
+            }
+        }
+      else
+        node = next_node (rs, node);
+    }
+}
+
+/* Scans RS for its first 1-bit and deletes up to REQUEST
+   contiguous 1-bits starting at that position.  Unless RS is
+   completely empty, sets *START to the position of the first
+   1-bit deleted and *WIDTH to the number actually deleted, which
+   may be less than REQUEST if fewer contiguous 1-bits were
+   present, and returns true.  If RS is completely empty, returns
+   false. */   
+bool
+range_set_allocate (struct range_set *rs, unsigned long int request,
+                    unsigned long int *start, unsigned long int *width) 
+{
+  struct range_set_node *node;
+  unsigned long int node_width;
+
+  assert (request > 0);
+
+  node = first_node (rs);
+  if (node == NULL)
+    return false;
+  node_width = node->end - node->start;
+
+  *start = node->start;
+  if (request < node_width) 
+    {
+      *width = request;
+      node->start += request;
+    }
+  else 
+    {
+      *width = node_width;
+      delete_node (rs, node);
+    }
+
+  rs->cache_end = 0;
+
+  return true;
+}
+
+/* Returns true if there is a 1-bit at the given POSITION in RS,
+   false otherwise. */
+bool
+range_set_contains (const struct range_set *rs_, unsigned long int position) 
+{
+  struct range_set *rs = (struct range_set *) rs_;
+  if (position < rs->cache_end && position >= rs->cache_start)
+    return rs->cache_value;
+  else 
+    {
+      struct range_set_node *node = find_node_le (rs, position);
+      if (node != NULL)
+        {
+          if (position < node->end) 
+            {
+              rs->cache_start = node->start;
+              rs->cache_end = node->end;
+              rs->cache_value = true;
+              return true; 
+            }
+          else 
+            {
+              struct range_set_node *next = next_node (rs, node);
+              rs->cache_start = node->end;
+              rs->cache_end = next != NULL ? next->start : ULONG_MAX;
+              rs->cache_value = false;
+              return false;
+            }
+        }
+      else
+        {
+          node = first_node (rs);
+          rs->cache_start = 0;
+          rs->cache_end = node != NULL ? node->start : ULONG_MAX;
+          rs->cache_value = false;
+          return false;
+        }
+    }
+}
+
+/* Returns true if RS contains no 1-bits, false otherwise. */
+bool
+range_set_is_empty (const struct range_set *rs) 
+{
+  return bt_count (&rs->bt) == 0;
+}
+
+/* Returns the node representing the first contiguous region of
+   1-bits in RS, or a null pointer if RS is empty.
+   Any call to range_set_insert, range_set_delete, or
+   range_set_allocate invalidates the returned node. */
+const struct range_set_node *
+range_set_first (const struct range_set *rs) 
+{
+  return first_node (rs);
+}
+
+/* If NODE is nonnull, returns the node representing the next
+   contiguous region of 1-bits in RS following NODE, or a null
+   pointer if NODE is the last region in RS.
+   If NODE is null, returns the first region in RS, as for
+   range_set_first.
+   Any call to range_set_insert, range_set_delete, or
+   range_set_allocate invalidates the returned node. */
+const struct range_set_node *
+range_set_next (const struct range_set *rs, const struct range_set_node *node) 
+{
+  return (node != NULL
+          ? next_node (rs, (struct range_set_node *) node)
+          : first_node (rs));
+}
+
+/* Returns the position of the first 1-bit in NODE. */
+unsigned long int
+range_set_node_get_start (const struct range_set_node *node) 
+{
+  return node->start;
+}
+
+/* Returns one past the position of the last 1-bit in NODE. */
+unsigned long int
+range_set_node_get_end (const struct range_set_node *node) 
+{
+  return node->end;
+}
+
+/* Returns the number of contiguous 1-bits in NODE. */
+unsigned long int
+range_set_node_get_width (const struct range_set_node *node) 
+{
+  return node->end - node->start;
+}
+\f
+/* Returns the range_set_node corresponding to the given
+   BT_NODE.  Returns a null pointer if BT_NODE is null. */
+static struct range_set_node *
+bt_to_rs_node (const struct bt_node *bt_node) 
+{
+  return bt_node ? bt_data (bt_node, struct range_set_node, bt_node) : NULL;
+}
+
+/* Compares `range_set_node's A and B and returns a strcmp-like
+   return value. */
+static int
+compare_range_set_nodes (const struct bt_node *a_,
+                         const struct bt_node *b_,
+                         const void *aux UNUSED) 
+{
+  const struct range_set_node *a = bt_to_rs_node (a_);
+  const struct range_set_node *b = bt_to_rs_node (b_);
+
+  return a->start < b->start ? -1 : a->start > b->start;
+}
+
+/* Returns the first range_set_node in RS,
+   or a null pointer if RS is empty. */
+static struct range_set_node *
+first_node (const struct range_set *rs) 
+{
+  return bt_to_rs_node (bt_first (&rs->bt));
+}
+
+/* Returns the next range_set_node in RS after NODE,
+   or a null pointer if NODE is the last node in RS. */
+static struct range_set_node *
+next_node (const struct range_set *rs, const struct range_set_node *node) 
+{
+  return bt_to_rs_node (bt_next (&rs->bt, &node->bt_node));
+}
+
+/* Deletes NODE from RS and frees it. */
+static void
+delete_node (struct range_set *rs, struct range_set_node *node) 
+{
+  bt_delete (&rs->bt, &node->bt_node);
+  free (node);
+}
+
+/* Deletes NODE from RS, frees it, and returns the following
+   node. */
+static struct range_set_node *
+delete_node_get_next (struct range_set *rs, struct range_set_node *node) 
+{
+  struct range_set_node *next = next_node (rs, node);
+  delete_node (rs, node);
+  return next;
+}
+
+/* Returns the node in RS that would be just before POSITION if a
+   range_set_node with `start' as POSITION were inserted.
+   Returns a null pointer if POSITION is before any existing node
+   in RS.  If POSITION would duplicate an existing region's
+   start, returns that region. */
+static struct range_set_node *
+find_node_le (const struct range_set *rs, unsigned long int position) 
+{
+  struct range_set_node tmp;
+  tmp.start = position;
+  return bt_to_rs_node (bt_find_le (&rs->bt, &tmp.bt_node));
+}
+
+/* Creates a new region with the given START and END, inserts the
+   region into RS, and returns the new region. */
+static struct range_set_node *
+insert_node (struct range_set *rs,
+             unsigned long int start, unsigned long int end) 
+{
+  struct range_set_node *node = xmalloc (sizeof *node);
+  struct bt_node *dummy;
+  node->start = start;
+  node->end = end;
+  dummy = bt_insert (&rs->bt, &node->bt_node);
+  assert (dummy == NULL);
+  return node;
+}
+
+/* Inserts the region START...END (exclusive) into RS given that
+   the new region is "just before" NODE, that is, if NODE is
+   nonnull, the new region is known not to overlap any node
+   preceding NODE, and START precedes NODE's start; if NODE is
+   null, then the new region is known to follow any and all
+   regions already in RS. */
+static void 
+insert_just_before (struct range_set *rs,
+                    unsigned long int start, unsigned long int end,
+                    struct range_set_node *node)
+{
+  assert (node == NULL || start < node->start);
+  if (node != NULL && end >= node->start)
+    {
+      /* New region overlaps NODE, so just extend NODE. */
+      node->start = start;
+      if (end > node->end) 
+        {
+          node->end = end;
+          merge_node_with_successors (rs, node); 
+        }
+    }
+  else 
+    {
+      /* New region does not overlap NODE. */
+      insert_node (rs, start, end); 
+    }
+}
+
+/* Merges NODE in RS with its successors. */
+static void
+merge_node_with_successors (struct range_set *rs, struct range_set_node *node) 
+{
+  struct range_set_node *next;
+  
+  while ((next = next_node (rs, node)) != NULL && node->end >= next->start)
+    {
+      if (next->end > node->end)
+        node->end = next->end;
+      delete_node (rs, next);
+    }
+}
+
+/* Destroys range set RS.
+   Helper function for range_set_create_pool. */
+static void
+destroy_pool (void *rs_) 
+{
+  struct range_set *rs = rs_;
+  rs->pool = NULL;
+  range_set_destroy (rs);
+}
diff --git a/src/libpspp/range-set.h b/src/libpspp/range-set.h
new file mode 100644 (file)
index 0000000..b6b60a4
--- /dev/null
@@ -0,0 +1,54 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+/* Bitmap, implemented as a balanced binary tree.
+
+   Each operation has O(lg N) cost, where N is the number of
+   contiguous regions of 1-bits in the bitmap.  Also, a cache
+   reduces the second and subsequent containment tests within a
+   single contiguous region to O(1). */
+
+#ifndef LIBPSPP_RANGE_SET_H
+#define LIBPSPP_RANGE_SET_H
+
+#include <stdbool.h>
+
+struct pool;
+
+struct range_set *range_set_create (void);
+struct range_set *range_set_create_pool (struct pool *);
+void range_set_destroy (struct range_set *);
+
+void range_set_insert (struct range_set *,
+                       unsigned long int start, unsigned long int width);
+void range_set_delete (struct range_set *,
+                       unsigned long int start, unsigned long int width);
+bool range_set_allocate (struct range_set *, unsigned long int request,
+                         unsigned long int *start, unsigned long int *width);
+bool range_set_contains (const struct range_set *, unsigned long int position);
+
+bool range_set_is_empty (const struct range_set *);
+
+const struct range_set_node *range_set_first (const struct range_set *);
+const struct range_set_node *range_set_next (const struct range_set *,
+                                             const struct range_set_node *);
+unsigned long int range_set_node_get_start (const struct range_set_node *);
+unsigned long int range_set_node_get_end (const struct range_set_node *);
+unsigned long int range_set_node_get_width (const struct range_set_node *);
+
+#endif /* libpspp/range-set.h */
diff --git a/src/libpspp/tower.c b/src/libpspp/tower.c
new file mode 100644 (file)
index 0000000..ead91e6
--- /dev/null
@@ -0,0 +1,225 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+
+#include <libpspp/tower.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/compiler.h>
+
+static struct tower_node *abt_to_tower_node (const struct abt_node *);
+static struct tower_node *first_node (const struct tower *);
+static struct tower_node *next_node (const struct tower *,
+                                     const struct tower_node *);
+static unsigned long int get_subtree_height (const struct abt_node *);
+static void reaugment_tower_node (struct abt_node *,
+                                  const struct abt_node *,
+                                  const struct abt_node *,
+                                  const void *aux);
+
+/* Initializes T as an empty tower. */
+void
+tower_init (struct tower *t) 
+{
+  abt_init (&t->abt, NULL, reaugment_tower_node, NULL);
+}
+
+/* Returns true if T contains no nodes, false otherwise. */
+bool
+tower_is_empty (const struct tower *t) 
+{
+  return t->abt.root == NULL;
+}
+
+/* Returns the total height of tower T. */
+unsigned long
+tower_height (const struct tower *t) 
+{
+  return get_subtree_height (t->abt.root);
+}
+
+/* Inserts node NEW with the specified HEIGHT into T just below
+   node UNDER, or at the top of T if UNDER is a null pointer. */
+void
+tower_insert (struct tower *t, unsigned long height, struct tower_node *new,
+              struct tower_node *under) 
+{
+  assert (height > 0);
+  new->height = height;
+  abt_insert_before (&t->abt, under ? &under->abt_node : NULL,
+                     &new->abt_node);
+}
+
+/* Deletes NODE from tower T. */
+struct tower_node *
+tower_delete (struct tower *t, struct tower_node *node) 
+{
+  struct tower_node *next = next_node (t, node);
+  abt_delete (&t->abt, &node->abt_node);
+  return next;
+}
+
+/* Changes the height of NODE in tower T to NEW_HEIGHT. */
+void
+tower_resize (struct tower *t, struct tower_node *node,
+              unsigned long new_height)
+{
+  assert (new_height > 0);
+  node->height = new_height;
+  abt_reaugmented (&t->abt, &node->abt_node);
+}
+
+/* Removes nodes FIRST through LAST (exclusive) from tower SRC
+   and splices them into tower DST just below node UNDER, or at
+   the top of DST if UNDER is a null pointer.
+
+   It might be better to implement an abt_splice function and
+   turn this into a wrapper, but the asymptotic performance would
+   be the same. */
+void
+tower_splice (struct tower *dst, struct tower_node *under,
+              struct tower *src,
+              struct tower_node *first, struct tower_node *last) 
+{
+  struct tower_node *next;
+  
+  /* Conceptually, DST == SRC is valid.
+     Practically, it's more difficult to get it right, and our
+     client code doesn't need it. */
+  assert (dst != src);
+
+  for (; first != last; first = next)
+    {
+      next = tower_delete (src, first);
+      abt_insert_before (&dst->abt, under ? &under->abt_node : NULL,
+                         &first->abt_node);
+    }
+}
+
+/* Returns the node at the given HEIGHT from the bottom of tower
+   T.  HEIGHT must be less than T's height (as returned by
+   tower_height).  Stores in *NODE_START the height of the bottom
+   of the returned node, which may be less than HEIGHT if HEIGHT
+   refers to the middle of a node instead of its bottom. */
+struct tower_node *
+tower_lookup (const struct tower *t,
+              unsigned long height,
+              unsigned long *node_start) 
+{
+  struct abt_node *p;
+
+  assert (height < tower_height (t));
+
+  *node_start = 0;
+  p = t->abt.root;
+  for (;;)
+    {
+      unsigned long left_height = get_subtree_height (p->down[0]);
+      if (height < left_height) 
+        {
+          /* Our goal height must lie within the left subtree. */
+          p = p->down[0];
+        }
+      else 
+        {
+          /* Our goal height cannot be in the left subtree. */
+          struct tower_node *node = abt_to_tower_node (p);
+          unsigned long int node_height = node->height;
+
+          height -= left_height;
+          *node_start += left_height;
+          if (height < node_height) 
+            {
+              /* Our goal height is in P. */
+              return node; 
+            }
+          else 
+            {
+              /* Our goal height is in the right subtree. */
+              p = p->down[1];
+              height -= node_height;
+              *node_start += node_height; 
+            }
+        }
+    }
+}
+
+/* Returns the node at height 0 in tower T, or a null pointer if
+   T is empty. */
+struct tower_node *
+tower_first (const struct tower *t) 
+{
+  return first_node (t);
+}
+
+/* If NODE is nonnull, returns the node just above NODE in tower
+   T, or a null pointer if NODE is the topmost node in T.
+   If NODE is null, acts like tower_first. */
+struct tower_node *
+tower_next (const struct tower *t, const struct tower_node *node) 
+{
+  return node != NULL ? next_node (t, node) : first_node (t);
+}
+\f
+/* Returns the tower node corresponding to the given ABT_NODE. */
+static struct tower_node *
+abt_to_tower_node (const struct abt_node *abt_node) 
+{
+  return abt_data (abt_node, struct tower_node, abt_node);
+}
+
+/* Returns the first node in TOWER. */
+static struct tower_node *
+first_node (const struct tower *t) 
+{
+  struct abt_node *abt_node = abt_first (&t->abt);
+  return abt_node != NULL ? abt_to_tower_node (abt_node) : NULL;
+}
+
+/* Returns the next node in TOWER after NODE. */
+static struct tower_node *
+next_node (const struct tower *t, const struct tower_node *node) 
+{
+  struct abt_node *abt_node = abt_next (&t->abt, &node->abt_node);
+  return abt_node != NULL ? abt_to_tower_node (abt_node) : NULL;
+}
+
+/* Returns the total height of the nodes in the subtree rooted at
+   P, or 0 if P is null. */
+static unsigned long int
+get_subtree_height (const struct abt_node *p) 
+{
+  return p != NULL ? abt_to_tower_node (p)->subtree_height : 0;
+}
+
+/* Recalculates the subtree_height of NODE based on its LEFT and
+   RIGHT children's subtree_heights. */
+static void
+reaugment_tower_node (struct abt_node *node_,
+                      const struct abt_node *left,
+                      const struct abt_node *right,
+                      const void *aux UNUSED)
+{
+  struct tower_node *node = abt_to_tower_node (node_);
+  node->subtree_height = node->height;
+  if (left != NULL)
+    node->subtree_height += abt_to_tower_node (left)->subtree_height;
+  if (right != NULL)
+    node->subtree_height += abt_to_tower_node (right)->subtree_height;
+}
diff --git a/src/libpspp/tower.h b/src/libpspp/tower.h
new file mode 100644 (file)
index 0000000..e0ee12a
--- /dev/null
@@ -0,0 +1,101 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+/* "Tower" data structure, implemented as an augmented binary
+   tree.
+
+   Imagine a tall stack of books on a table; actually, call it a
+   "tower" of books because "stack" is already taken.  If you're
+   careful enough and strong enough, you can pull individual
+   books out of the stack, as well as insert new books between
+   existing ones or at the bottom or top of the stack.
+
+   At any given time, you can refer to a book in the tower by
+   measuring the book's height above the tower in some unit,
+   e.g. mm.  This isn't necessarily equivalent to the number of
+   books in the tower below the book in question, like an array
+   index, because the books in the stack aren't necessarily all
+   the same thickness: some might be as thin as K&R and others as
+   thick as _Introduction to Algorithms_.
+
+   This is the analogy behind this data structure.  Each node in
+   the data structure has a "thickness", which is actually called
+   the node's "height" because "thickness" is just too awkward a
+   name.  The primary way to look up nodes is by a height from
+   the bottom of the tower; any height within a node retrieves
+   that node, not just the distance to the bottom of the node.
+   You can insert a new node between any two existing nodes, or
+   at either end, which shifts up the height of all the nodes
+   above it.  You can also delete any node, which shifts down the
+   height of all the nodes above it. */
+
+#ifndef LIBPSPP_TOWER_H
+#define LIBPSPP_TOWER_H
+
+#include <stdbool.h>
+#include <libpspp/abt.h>
+
+/* Returns the data structure corresponding to the given NODE,
+   assuming that NODE is embedded as the given MEMBER name in
+   data type STRUCT. */
+#define tower_data(NODE, STRUCT, MEMBER)                            \
+        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+
+/* A node within a tower. */
+struct tower_node 
+  {
+    struct abt_node abt_node;         /* ABT node. */
+    unsigned long int subtree_height; /* Node's plus descendants' heights. */
+    unsigned long int height;         /* Height. */
+  };
+
+/* Returns the height of a tower node. */
+static inline unsigned long
+tower_node_get_height (const struct tower_node *node) 
+{
+  return node->height;
+}
+
+/* A tower. */
+struct tower 
+  {
+    struct abt abt;              /* Tree. */
+  };
+
+void tower_init (struct tower *);
+
+bool tower_is_empty (const struct tower *);
+unsigned long int tower_height (const struct tower *);
+
+void tower_insert (struct tower *, unsigned long int height,
+                   struct tower_node *new, struct tower_node *under);
+struct tower_node *tower_delete (struct tower *, struct tower_node *);
+void tower_resize (struct tower *, struct tower_node *,
+                   unsigned long int new_height);
+void tower_splice (struct tower *dst, struct tower_node *under,
+                   struct tower *src,
+                   struct tower_node *first, struct tower_node *last);
+
+struct tower_node *tower_lookup (const struct tower *,
+                                 unsigned long int level,
+                                 unsigned long int *node_start);
+struct tower_node *tower_first (const struct tower *);
+struct tower_node *tower_next (const struct tower *,
+                               const struct tower_node *);
+
+#endif /* libpspp/tower.h */
index e4cda349a7b9991364a10a0e3417f59dbab07ba3..62057ed7d3cbdd526b801e82931fd633a2016289 100644 (file)
@@ -1,3 +1,16 @@
+2007-04-03  Ben Pfaff  <blp@gnu.org>
+
+       Apply patches #5828, #5837, #5841, #5843.
+
+       * automake.mk (tests_libpspp_bt_test_LDADD): Add range-map-test,
+       range-set-test, tower-test.
+
+       * libpspp/range-map-test.c: New test.
+
+       * libpspp/range-set-test.c: New test.
+
+       * libpspp/tower-test.c: New test.
+
 2007-03-31  Ben Pfaff  <blp@gnu.org>
 
        * automake.mk (tests_libpspp_bt_test_LDADD): Add tests/libpspp/bt.
index d65d0e4906b63f72c8584db605b6ab7a12e04927..51bf743ffb00db1db7792f5b651cd0090bfa7ad1 100644 (file)
@@ -138,7 +138,10 @@ nodist_TESTS = \
        tests/libpspp/heap-test \
        tests/libpspp/ll-test \
        tests/libpspp/llx-test \
-       tests/libpspp/sparse-array-test
+       tests/libpspp/range-map-test \
+       tests/libpspp/range-set-test \
+       tests/libpspp/sparse-array-test \
+       tests/libpspp/tower-test
 
 TESTS = $(dist_TESTS) $(nodist_TESTS)
 
@@ -171,7 +174,7 @@ tests_libpspp_abt_test_SOURCES = \
        src/libpspp/abt.c \
        src/libpspp/abt.h \
        tests/libpspp/abt-test.c
-tests_libpspp_abt_test_LDADD = gl/libgl.la
+tests_libpspp_abt_test_LDADD = gl/libgl.la @LIBINTL@
 tests_libpspp_abt_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
 
 tests_libpspp_bt_test_SOURCES = \
@@ -181,6 +184,37 @@ tests_libpspp_bt_test_SOURCES = \
 tests_libpspp_bt_test_LDADD = gl/libgl.la
 tests_libpspp_bt_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
 
+tests_libpspp_range_map_test_SOURCES = \
+       src/libpspp/bt.c \
+       src/libpspp/bt.h \
+       src/libpspp/range-map.c \
+       src/libpspp/range-map.h \
+       tests/libpspp/range-map-test.c
+tests_libpspp_range_map_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_range_map_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
+
+tests_libpspp_range_set_test_SOURCES = \
+       src/libpspp/bt.c \
+       src/libpspp/bt.h \
+       src/libpspp/pool.c \
+       src/libpspp/pool.h \
+       src/libpspp/range-set.c \
+       src/libpspp/range-set.h \
+       tests/libpspp/range-set-test.c
+tests_libpspp_range_set_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_range_set_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
+
+tests_libpspp_tower_test_SOURCES = \
+       src/libpspp/abt.c \
+       src/libpspp/abt.h \
+       src/libpspp/pool.c \
+       src/libpspp/pool.h \
+       src/libpspp/tower.c \
+       src/libpspp/tower.h \
+       tests/libpspp/tower-test.c
+tests_libpspp_tower_test_LDADD = gl/libgl.la @LIBINTL@
+tests_libpspp_tower_test_CPPFLAGS = $(AM_CPPFLAGS) -DASSERT_LEVEL=10
+
 tests_libpspp_sparse_array_test_SOURCES = \
        src/libpspp/sparse-array.c \
        src/libpspp/sparse-array.h \
index 6a165929f7d2e46f10d20b3d632580ffec13ae68..ce857b050dd1cdbd4147cbf6e623f309ac0bc297 100644 (file)
@@ -336,22 +336,25 @@ check_abt (struct abt *abt, const int data[], size_t cnt)
   order = xmemdup (data, cnt * sizeof *data);
   qsort (order, cnt, sizeof *order, compare_ints_noaux);
 
-  for (i = 0; i < cnt; i++)
+  if (abt->compare != NULL) 
     {
-      struct abt_node *p;
+      for (i = 0; i < cnt; i++)
+        {
+          struct abt_node *p;
       
-      e.data = data[i];
-      if (rand () % 2)
-        p = abt_find (abt, &e.node);
-      else
-        p = abt_insert (abt, &e.node);
-      check (p != NULL);
-      check (p != &e.node);
-      check (abt_node_to_element (p)->data == data[i]);
-    }
+          e.data = data[i];
+          if (rand () % 2)
+            p = abt_find (abt, &e.node);
+          else
+            p = abt_insert (abt, &e.node);
+          check (p != NULL);
+          check (p != &e.node);
+          check (abt_node_to_element (p)->data == data[i]);
+        }
 
-  e.data = -1;
-  check (abt_find (abt, &e.node) == NULL);
+      e.data = -1;
+      check (abt_find (abt, &e.node) == NULL);
+    }
 
   check_levels (abt->root);
   check_augmentations (abt->root);
@@ -381,14 +384,59 @@ check_abt (struct abt *abt, const int data[], size_t cnt)
   free (order);
 }
 
+/* Ways that nodes can be inserted. */
+enum insertion_method 
+  {
+    INSERT,             /* With abt_insert. */
+    INSERT_AFTER,       /* With abt_insert_after. */
+    INSERT_BEFORE       /* With abt_insert_before. */
+  };
+
+/* Inserts INSERT into ABT with the given METHOD. */
+static void
+insert_node (struct abt *abt, struct element *insert,
+             enum insertion_method method) 
+{
+  if (method == INSERT) 
+    check (abt_insert (abt, &insert->node) == NULL);
+  else 
+    {
+      struct abt_node *p = abt->root;
+      int dir = 0;
+      if (p != NULL)
+        for (;;) 
+          {
+            dir = insert->data > abt_node_to_element (p)->data;
+            if (p->down[dir] == NULL)
+              break;
+            p = p->down[dir];
+          }
+      if (method == INSERT_AFTER) 
+        {
+          if (p != NULL && (dir != 1 || p->down[1] != NULL))
+            p = abt_prev (abt, p);
+          abt_insert_after (abt, p, &insert->node); 
+        }
+      else
+        {
+          if (p != NULL && (dir != 0 || p->down[0] != NULL))
+            p = abt_next (abt, p);
+          abt_insert_before (abt, p, &insert->node); 
+        }
+    }
+}
+
+
 /* Inserts the CNT values from 0 to CNT - 1 (inclusive) into an
-   ABT in the order specified by INSERTIONS, then deletes them in
-   the order specified by DELETIONS, checking the ABT's contents
-   for correctness after each operation. */
+   ABT in the order specified by INSERTIONS using the given
+   METHOD, then deletes them in the order specified by DELETIONS,
+   checking the ABT's contents for correctness after each
+   operation. */
 static void
-test_insert_delete (const int insertions[],
-                    const int deletions[],
-                    size_t cnt) 
+do_test_insert_delete (enum insertion_method method,
+                       const int insertions[],
+                       const int deletions[],
+                       size_t cnt) 
 {
   struct element *elements;
   struct abt abt;
@@ -398,11 +446,12 @@ test_insert_delete (const int insertions[],
   for (i = 0; i < cnt; i++)
     elements[i].data = i;
 
-  abt_init (&abt, compare_elements, reaugment_elements, &aux_data);
+  abt_init (&abt, method == INSERT ? compare_elements : NULL,
+            reaugment_elements, &aux_data);
   check_abt (&abt, NULL, 0);
   for (i = 0; i < cnt; i++)
     {
-      check (abt_insert (&abt, &elements[insertions[i]].node) == NULL);
+      insert_node (&abt, &elements[insertions[i]], method);
       check_abt (&abt, insertions, i + 1);
     }
   for (i = 0; i < cnt; i++)
@@ -413,6 +462,20 @@ test_insert_delete (const int insertions[],
 
   free (elements);
 }
+
+/* Inserts the CNT values from 0 to CNT - 1 (inclusive) into an
+   ABT in the order specified by INSERTIONS, then deletes them in
+   the order specified by DELETIONS, checking the ABT's contents
+   for correctness after each operation. */
+static void
+test_insert_delete (const int insertions[],
+                    const int deletions[],
+                    size_t cnt) 
+{
+  do_test_insert_delete (INSERT, insertions, deletions, cnt);
+  do_test_insert_delete (INSERT_AFTER, insertions, deletions, cnt);
+  do_test_insert_delete (INSERT_BEFORE, insertions, deletions, cnt);
+}
 \f
 /* Inserts values into an ABT in each possible order, then
    removes them in each possible order, up to a specified maximum
diff --git a/tests/libpspp/range-map-test.c b/tests/libpspp/range-map-test.c
new file mode 100644 (file)
index 0000000..2cb0ce6
--- /dev/null
@@ -0,0 +1,515 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+/* This is a test program for the routines defined in
+   range-map.c.  This test program aims to be as comprehensive as
+   possible.  With -DNDEBUG, "gcov -b" should report 100%
+   coverage of lines and branches in range-map.c routines.
+   (Without -DNDEBUG, branches caused by failed assertions will
+   not be taken.)  "valgrind --leak-check=yes
+   --show-reachable=yes" should give a clean report, both with
+   and without -DNDEBUG. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/range-map.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/compiler.h>
+
+#include "xalloc.h"
+\f
+/* Currently running test. */
+static const char *test_name;
+
+/* Exit with a failure code.
+   (Place a breakpoint on this function while debugging.) */
+static void
+check_die (void) 
+{
+  exit (EXIT_FAILURE);   
+}
+
+/* If OK is not true, prints a message about failure on the
+   current source file and the given LINE and terminates. */
+static void
+check_func (bool ok, int line) 
+{
+  if (!ok) 
+    {
+      printf ("Check failed in %s test at %s, line %d\n",
+              test_name, __FILE__, line);
+      check_die ();
+    }
+}
+
+/* Verifies that EXPR evaluates to true.
+   If not, prints a message citing the calling line number and
+   terminates. */
+#define check(EXPR) check_func ((EXPR), __LINE__)
+\f
+/* Swaps *A and *B. */
+static void
+swap (int *a, int *b) 
+{
+  int t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Reverses the order of the CNT integers starting at VALUES. */
+static void
+reverse (int *values, size_t cnt)
+{
+  size_t i = 0;
+  size_t j = cnt;
+
+  while (j > i)
+    swap (&values[i++], &values[--j]);
+}
+
+/* Arranges the CNT blocks in VALUES into the lexicographically
+   next greater permutation.  Returns true if successful.
+   If VALUES is already the lexicographically greatest
+   permutation of its blocks (i.e. ordered from greatest to
+   smallest), arranges them into the lexicographically least
+   permutation (i.e. ordered from smallest to largest) and
+   returns false. */
+static bool
+next_permutation (int *values, size_t cnt)
+{
+  if (cnt > 0)
+    {
+      size_t i = cnt - 1;
+      while (i != 0) 
+        {
+          i--;
+          if (values[i] < values[i + 1])
+            {
+              size_t j;
+              for (j = cnt - 1; values[i] >= values[j]; j--)
+                continue;
+              swap (values + i, values + j);
+              reverse (values + (i + 1), cnt - (i + 1));
+              return true;
+            } 
+        }
+      
+      reverse (values, cnt);
+    }
+  
+  return false;
+}
+
+/* Returns N!. */
+static unsigned int
+factorial (unsigned int n) 
+{
+  unsigned int value = 1;
+  /* Disallow N values that overflow on 32-bit machines. */
+  assert (n <= 12);
+  for (; n > 1; )
+    value *= n--;
+  return value;
+}
+
+/* Tests whether PARTS is a K-part integer composition of N.
+   Returns true if so, false otherwise. */
+static bool UNUSED
+is_k_composition (int n, int k, const int parts[]) 
+{
+  int sum;
+  int i;
+
+  sum = 0;
+  for (i = 0; i < k; i++)
+    {
+      if (parts[i] < 1 || parts[i] > n)
+        return false;
+      sum += parts[i];
+    }
+  return sum == n;
+}
+
+/* Advances the K-part integer composition of N stored in PARTS
+   to the next lexicographically greater one.
+   Returns true if successful, false if the composition was
+   already the greatest K-part composition of N (in which case
+   PARTS is unaltered). */
+static bool
+next_k_composition (int n UNUSED, int k, int parts[]) 
+{
+  int x, i;
+
+  assert (is_k_composition (n, k, parts));
+  if (k == 1)
+    return false;
+
+  for (i = k - 1; i > 0; i--)
+    if (parts[i] > 1)
+      break;
+  if (i == 0)
+    return false;
+
+  x = parts[i] - 1;
+  parts[i] = 1;
+  parts[i - 1]++;
+  parts[k - 1] = x;
+
+  assert (is_k_composition (n, k, parts));
+  return true;
+}
+
+/* Sets the K integers in PARTS to the lexicographically first
+   K-part composition of N. */
+static void
+first_k_composition (int n, int k, int parts[]) 
+{
+  int i;
+
+  assert (n >= k);
+
+  for (i = 0; i < k; i++)
+    parts[i] = 1;
+  parts[k - 1] += n - k;
+}
+
+/* Advances *K and PARTS to the next integer composition of N.
+   Compositions are ordered from shortest to longest and in
+   lexicographical order within a given length.
+   Before the first call, initialize *K to 0.
+   After each successful call, *K contains the length of the
+   current composition and the *K blocks in PARTS contain its
+   parts.
+   Returns true if successful, false if the set of compositions
+   has been exhausted. */
+static bool
+next_composition (int n, int *k, int parts[]) 
+{
+  if (*k >= 1 && next_k_composition (n, *k, parts))
+    return true;
+  else if (*k < n)
+    {
+      first_k_composition (n, ++*k, parts);
+      return true;
+    }
+  else
+    return false;
+}
+\f
+/* Test data element. */
+struct element
+  {
+    struct range_map_node node; /* Embedded tower block. */
+    int x;                      /* Primary value. */
+  };
+
+static struct element *
+range_map_node_to_element (struct range_map_node *node) 
+{
+  return range_map_data (node, struct element, node);
+}
+
+/* Element we expect to find. */
+struct expected_element 
+  {
+    int x;                      /* Primary value. */
+    unsigned long int start;    /* Start of region. */
+    unsigned long int end;      /* End of region. */
+  };
+
+/* Compares expected_element A and B and returns a strcmp()-type
+   result. */
+static int
+compare_expected_element (const void *a_, const void *b_) 
+{
+  const struct expected_element *a = (const struct expected_element *) a_;
+  const struct expected_element *b = (const struct expected_element *) b_;
+  return a->start < b->start ? -1 : a->start > b->start;
+}
+
+/* Checks that RM contains the ELEM_CNT elements described by
+   ELEMENTS[]. */
+static void
+check_range_map (struct range_map *rm,
+                 struct expected_element elements[], size_t elem_cnt) 
+{
+  struct expected_element *sorted;
+  struct range_map_node *node;
+  size_t i;
+
+  sorted = xnmalloc (elem_cnt, sizeof *sorted);
+  memcpy (sorted, elements, elem_cnt * sizeof *elements);
+  qsort (sorted, elem_cnt, sizeof *sorted, compare_expected_element);
+  
+  check (range_map_is_empty (rm) == (elem_cnt == 0));
+
+  for (i = 0; i < elem_cnt; i++) 
+    {
+      struct expected_element *e = &sorted[i];
+      unsigned long int position;
+
+      /* Check that range_map_lookup finds all the positions
+         within the element. */
+      for (position = e->start; position < e->end; position++) 
+        {
+          struct range_map_node *found = range_map_lookup (rm, position);
+          check (found != NULL);
+          check (range_map_node_to_element (found)->x == e->x);
+          check (range_map_node_get_start (found) == e->start);
+          check (range_map_node_get_end (found) == e->end);
+          check (range_map_node_get_width (found) == e->end - e->start);
+        }
+
+      /* If there shouldn't be any elements in the positions just
+         before or after the element, verify that
+         range_map_lookup doesn't find any there. */
+      if (e->start > 0 && (i == 0 || e[-1].end < e->start))
+        check (range_map_lookup (rm, e->start - 1) == NULL);
+      if (i == elem_cnt - 1 || e->end < e[1].start)
+        check (range_map_lookup (rm, e->end) == NULL);
+    }
+
+  for (node = (rand () % 2 ? range_map_first (rm) : range_map_next (rm, NULL)),
+         i = 0;
+       node != NULL;
+       node = range_map_next (rm, node), i++) 
+    {
+      struct expected_element *e = &sorted[i];
+      check (range_map_node_to_element (node)->x == e->x);
+    }
+  check (i == elem_cnt);
+
+  free (sorted);
+}
+\f
+/* Tests inserting all possible sets of ranges into a range map
+   in all possible orders, up to a specified maximum overall
+   range. */
+static void
+test_insert (void) 
+{
+  const int max_range = 7;
+  int cnt;
+
+  for (cnt = 1; cnt <= max_range; cnt++) 
+    {
+      unsigned int composition_cnt;
+      struct expected_element *expected;
+      int *widths;
+      int elem_cnt;
+      int *order;
+      struct element *elements;
+      
+      expected = xnmalloc (cnt, sizeof *expected);
+      widths = xnmalloc (cnt, sizeof *widths);
+      order = xnmalloc (cnt, sizeof *order);
+      elements = xnmalloc (cnt, sizeof *elements);
+
+      elem_cnt = 0;
+      composition_cnt = 0;
+      while (next_composition (cnt, &elem_cnt, widths)) 
+        {
+          int i, j;
+          unsigned int permutation_cnt;
+
+          for (i = 0; i < elem_cnt; i++) 
+            order[i] = i;
+
+          permutation_cnt = 0;
+          while (permutation_cnt == 0 || next_permutation (order, elem_cnt)) 
+            {
+              struct range_map rm;
+
+              /* Inserts the elem_cnt elements with the given
+                 widths[] into T in the order given by order[]. */
+              range_map_init (&rm);
+              for (i = 0; i < elem_cnt; i++) 
+                {
+                  unsigned long int start, end;
+                  int idx;
+
+                  idx = order[i];
+                  elements[idx].x = idx;
+
+                  /* Find start and end of element. */
+                  start = 0;
+                  for (j = 0; j < idx; j++)
+                    start += widths[j];
+                  end = start + widths[j];
+
+                  /* Insert. */
+                  range_map_insert (&rm, start, end - start,
+                                    &elements[idx].node);
+
+                  /* Check map contents. */
+                  expected[i].x = idx;
+                  expected[i].start = start;
+                  expected[i].end = end;
+                  check_range_map (&rm, expected, i + 1);
+                }
+              permutation_cnt++;
+            }
+          check (permutation_cnt == factorial (elem_cnt));
+          
+          composition_cnt++;
+        }
+      check (composition_cnt == 1 << (cnt - 1));
+
+      free (expected);
+      free (widths);
+      free (order);
+      free (elements);
+    }
+}
+
+/* Tests deleting ranges from a range map in all possible orders,
+   up to a specified maximum overall range. */
+static void
+test_delete (int gap) 
+{
+  const int max_range = 7;
+  int cnt;
+
+  for (cnt = 1; cnt <= max_range; cnt++) 
+    {
+      unsigned int composition_cnt;
+      struct expected_element *expected;
+      int *widths;
+      int elem_cnt;
+      int *order;
+      struct element *elements;
+      
+      expected = xnmalloc (cnt, sizeof *expected);
+      widths = xnmalloc (cnt, sizeof *widths);
+      order = xnmalloc (cnt, sizeof *order);
+      elements = xnmalloc (cnt, sizeof *elements);
+
+      elem_cnt = 0;
+      composition_cnt = 0;
+      while (next_composition (cnt, &elem_cnt, widths)) 
+        {
+          int i, j;
+          unsigned int permutation_cnt;
+
+          for (i = 0; i < elem_cnt; i++) 
+            order[i] = i;
+
+          permutation_cnt = 0;
+          while (permutation_cnt == 0 || next_permutation (order, elem_cnt)) 
+            {
+              struct range_map rm;
+              unsigned long int start;
+
+              /* Insert all the elements. */
+              range_map_init (&rm);
+              start = 0;
+              for (i = 0; i < elem_cnt; i++) 
+                {
+                  int width = widths[i] > gap ? widths[i] - gap : widths[i];
+                  unsigned long int end = start + width;
+
+                  elements[i].x = i;
+                  range_map_insert (&rm, start, end - start,
+                                    &elements[i].node);
+
+                  for (j = 0; ; j++) 
+                    {
+                      assert (j < elem_cnt);
+                      if (order[j] == i) 
+                        {
+                          expected[j].x = i;
+                          expected[j].start = start;
+                          expected[j].end = end;
+                          break;
+                        }
+                    }
+                  
+                  start += widths[i];
+                }
+              check_range_map (&rm, expected, elem_cnt);
+
+              /* Delete the elements in the specified order. */
+              for (i = 0; i < elem_cnt; i++) 
+                {
+                  range_map_delete (&rm, &elements[order[i]].node);
+                  check_range_map (&rm, expected + i + 1, elem_cnt - i - 1);
+                }
+
+              permutation_cnt++;
+            }
+          check (permutation_cnt == factorial (elem_cnt));
+          
+          composition_cnt++;
+        }
+      check (composition_cnt == 1 << (cnt - 1));
+
+      free (expected);
+      free (widths);
+      free (order);
+      free (elements);
+    }
+}
+
+/* Tests deleting ranges from a range map filled with contiguous
+   ranges in all possible orders, up to a specified maximum
+   overall range. */
+static void
+test_delete_contiguous (void) 
+{
+  test_delete (0);
+}
+
+/* Tests deleting ranges from a range map filled with ranges
+   sometimes separated by gaps in all possible orders, up to a
+   specified maximum overall range. */
+static void
+test_delete_gaps (void) 
+{
+  test_delete (1);
+}
+\f
+/* Main program. */
+
+/* Runs TEST_FUNCTION and prints a message about NAME. */
+static void
+run_test (void (*test_function) (void), const char *name) 
+{
+  test_name = name;
+  putchar ('.');
+  fflush (stdout);
+  test_function ();
+}
+
+int
+main (void) 
+{
+  run_test (test_insert, "insert");
+  run_test (test_delete_contiguous, "delete from contiguous ranges");
+  run_test (test_delete_gaps, "delete from ranges separated by gaps");
+  putchar ('\n');
+
+  return 0;
+}
diff --git a/tests/libpspp/range-set-test.c b/tests/libpspp/range-set-test.c
new file mode 100644 (file)
index 0000000..b1c7451
--- /dev/null
@@ -0,0 +1,335 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+/* This is a test program for the routines defined in
+   range-set.c.  This test program aims to be as comprehensive as
+   possible.  With -DNDEBUG, "gcov -b" should report 100%
+   coverage of lines and branches in range-set.c routines.
+   (Without -DNDEBUG, branches caused by failed assertions will
+   not be taken.)  "valgrind --leak-check=yes
+   --show-reachable=yes" should give a clean report, both with
+   and without -DNDEBUG. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/range-set.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/compiler.h>
+#include <libpspp/pool.h>
+
+#include "xalloc.h"
+\f
+/* Currently running test. */
+static const char *test_name;
+
+/* Exit with a failure code.
+   (Place a breakpoint on this function while debugging.) */
+static void
+check_die (void) 
+{
+  exit (EXIT_FAILURE);   
+}
+
+/* If OK is not true, prints a message about failure on the
+   current source file and the given LINE and terminates. */
+static void
+check_func (bool ok, int line) 
+{
+  if (!ok) 
+    {
+      printf ("Check failed in %s test at %s, line %d\n",
+              test_name, __FILE__, line);
+      check_die ();
+    }
+}
+
+/* Verifies that EXPR evaluates to true.
+   If not, prints a message citing the calling line number and
+   terminates. */
+#define check(EXPR) check_func ((EXPR), __LINE__)
+\f
+/* A contiguous region. */
+struct region 
+  {
+    unsigned long int start;    /* Start of region. */
+    unsigned long int end;      /* One past the end. */
+  };
+
+/* Number of bits in an unsigned int. */
+#define UINT_BIT (CHAR_BIT * sizeof (unsigned int))
+
+/* Returns the number of contiguous 1-bits in X starting from bit
+   0.
+   This implementation is designed to be obviously correct, not
+   to be efficient. */
+static int
+count_one_bits (unsigned long int x) 
+{
+  int count = 0;
+  while (x & 1) 
+    {
+      count++;
+      x >>= 1;
+    }
+  return count;
+}
+
+/* Searches the bits in PATTERN from right to left starting from
+   bit OFFSET for one or more 1-bits.  If any are found, sets
+   *START to the bit index of the first and *WIDTH to the number
+   of contiguous 1-bits and returns true.  Otherwise, returns
+   false.
+   This implementation is designed to be obviously correct, not
+   to be efficient. */
+static bool
+next_region (unsigned int pattern, unsigned int offset,
+             unsigned long int *start, unsigned long int *width) 
+{
+  unsigned int i;
+
+  assert (offset <= UINT_BIT);
+  for (i = offset; i < UINT_BIT; i++)
+    if (pattern & (1u << i)) 
+      {
+        *start = i;
+        *width = count_one_bits (pattern >> i);
+        return true;
+      }
+  return false;
+}
+
+/* Prints the regions in RS to stdout. */
+static void UNUSED
+print_regions (const struct range_set *rs)
+{
+  const struct range_set_node *node;
+
+  printf ("result:");
+  for (node = range_set_first (rs); node != NULL;
+       node = range_set_next (rs, node)) 
+    printf (" (%lu,%lu)",
+            range_set_node_get_start (node), range_set_node_get_end (node));
+  printf ("\n");
+}
+
+/* Checks that the regions in RS match the bits in PATTERN. */
+static void
+check_pattern (const struct range_set *rs, unsigned int pattern) 
+{
+  const struct range_set_node *node;
+  unsigned long int start, width;
+  int i;
+  
+  for (node = rand () % 2 ? range_set_first (rs) : range_set_next (rs, NULL),
+         start = width = 0;
+       next_region (pattern, start + width, &start, &width);
+       node = range_set_next (rs, node)) 
+    {
+      check (node != NULL);
+      check (range_set_node_get_start (node) == start);
+      check (range_set_node_get_end (node) == start + width);
+      check (range_set_node_get_width (node) == width);
+    }
+  check (node == NULL);
+
+  for (i = 0; i < UINT_BIT; i++)
+    check (range_set_contains (rs, i) == ((pattern & (1u << i)) != 0));
+  check (!range_set_contains (rs,
+                              UINT_BIT + rand () % (ULONG_MAX - UINT_BIT)));
+
+  check (range_set_is_empty (rs) == (pattern == 0));
+}
+
+/* Creates and returns a range set that contains regions for the
+   bits set in PATTERN. */
+static struct range_set *
+make_pattern (unsigned int pattern) 
+{
+  unsigned long int start = 0;
+  unsigned long int width = 0;
+  struct range_set *rs = range_set_create_pool (NULL);
+  while (next_region (pattern, start + width, &start, &width))
+    range_set_insert (rs, start, width);
+  check_pattern (rs, pattern);
+  return rs;
+}
+
+/* Returns an unsigned int with bits OFS...OFS+CNT (exclusive)
+   set to 1, other bits set to 0. */
+static unsigned int
+bit_range (unsigned int ofs, unsigned int cnt) 
+{
+  assert (ofs < UINT_BIT);
+  assert (cnt <= UINT_BIT);
+  assert (ofs + cnt <= UINT_BIT);
+
+  return cnt < UINT_BIT ? ((1u << cnt) - 1) << ofs : UINT_MAX;
+}
+\f
+/* Tests inserting all possible patterns into all possible range
+   sets (up to a small maximum number of bits). */
+static void
+test_insert (void) 
+{
+  const int positions = 9;
+  unsigned int init_pat;
+  int i, j;
+  
+  for (init_pat = 0; init_pat < (1u << positions); init_pat++) 
+    for (i = 0; i < positions + 1; i++)
+      for (j = i; j <= positions + 1; j++)
+        {
+          struct range_set *rs;
+          unsigned int final_pat;
+
+          rs = make_pattern (init_pat);
+          range_set_insert (rs, i, j - i);
+          final_pat = init_pat | bit_range (i, j - i);
+          check_pattern (rs, final_pat);
+          range_set_destroy (rs);
+        }
+}
+
+/* Tests deleting all possible patterns from all possible range
+   sets (up to a small maximum number of bits). */
+static void
+test_delete (void) 
+{
+  const int positions = 9;
+  unsigned int init_pat;
+  int i, j;
+  
+  for (init_pat = 0; init_pat < (1u << positions); init_pat++) 
+    for (i = 0; i < positions + 1; i++)
+      for (j = i; j <= positions + 1; j++)
+        {
+          struct range_set *rs;
+          unsigned int final_pat;
+
+          rs = make_pattern (init_pat);
+          range_set_delete (rs, i, j - i);
+          final_pat = init_pat & ~bit_range (i, j - i);
+          check_pattern (rs, final_pat);
+          range_set_destroy (rs);
+        }
+}
+
+/* Tests all possible allocation in all possible range sets (up
+   to a small maximum number of bits). */
+static void
+test_allocate (void)
+{
+  const int positions = 9;
+  unsigned int init_pat;
+  int request;
+  
+  for (init_pat = 0; init_pat < (1u << positions); init_pat++) 
+    for (request = 1; request <= positions + 1; request++)
+      {
+        struct range_set *rs;
+        unsigned long int start, width, expect_start, expect_width;
+        bool success, expect_success;
+        unsigned int final_pat;
+        int i;
+
+        /* Figure out expected results. */
+        expect_success = false;
+        expect_start = expect_width = 0;
+        final_pat = init_pat;
+        for (i = 0; i < positions; i++)
+          if (init_pat & (1u << i))
+            {
+              expect_success = true;
+              expect_start = i;
+              expect_width = count_one_bits (init_pat >> i);
+              if (expect_width > request)
+                expect_width = request;
+              final_pat &= ~bit_range (expect_start, expect_width);
+              break;
+            }
+
+        /* Test. */
+        rs = make_pattern (init_pat);
+        success = range_set_allocate (rs, request, &start, &width);
+        check_pattern (rs, final_pat);
+        range_set_destroy (rs);
+
+        /* Check results. */
+        check (success == expect_success);
+        if (expect_success) 
+          {
+            check (start == expect_start);
+            check (width == expect_width);
+          }
+      }
+}
+
+/* Tests freeing a range set through a pool. */
+static void
+test_pool (void) 
+{
+  struct pool *pool;
+  struct range_set *rs;
+
+  /* Destroy the range set, then the pool.
+     Makes sure that this doesn't cause a double-free. */
+  pool = pool_create ();
+  rs = range_set_create_pool (pool);
+  range_set_insert (rs, 1, 10);
+  range_set_destroy (rs);
+  pool_destroy (pool);
+  
+  /* Just destroy the pool.
+     Makes sure that this doesn't cause a leak. */
+  pool = pool_create ();
+  rs = range_set_create_pool (pool);
+  range_set_insert (rs, 1, 10);
+  pool_destroy (pool);
+}
+\f
+/* Main program. */
+
+/* Runs TEST_FUNCTION and prints a message about NAME. */
+static void
+run_test (void (*test_function) (void), const char *name) 
+{
+  test_name = name;
+  putchar ('.');
+  fflush (stdout);
+  test_function ();
+}
+
+int
+main (void) 
+{
+  run_test (test_insert, "insert");
+  run_test (test_delete, "delete");
+  run_test (test_allocate, "allocate");
+  run_test (test_pool, "pool");
+  putchar ('\n');
+
+  return 0;
+}
diff --git a/tests/libpspp/tower-test.c b/tests/libpspp/tower-test.c
new file mode 100644 (file)
index 0000000..f78fb89
--- /dev/null
@@ -0,0 +1,689 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+   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 program 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
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+/* This is a test program for the routines defined in tower.c.
+   This test program aims to be as comprehensive as possible.
+   With -DNDEBUG, "gcov -b" should report 100% coverage of lines
+   and branches in tower.c routines.  (Without -DNDEBUG, branches
+   caused by failed assertions will not be taken.)  "valgrind
+   --leak-check=yes --show-reachable=yes" should give a clean
+   report, both with and without -DNDEBUG. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libpspp/tower.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libpspp/compiler.h>
+
+#include "xalloc.h"
+\f
+/* Currently running test. */
+static const char *test_name;
+
+/* Exit with a failure code.
+   (Place a breakpoint on this function while debugging.) */
+static void
+check_die (void) 
+{
+  exit (EXIT_FAILURE);   
+}
+
+/* If OK is not true, prints a message about failure on the
+   current source file and the given LINE and terminates. */
+static void
+check_func (bool ok, int line) 
+{
+  if (!ok) 
+    {
+      printf ("Check failed in %s test at %s, line %d\n",
+              test_name, __FILE__, line);
+      check_die ();
+    }
+}
+
+/* Verifies that EXPR evaluates to true.
+   If not, prints a message citing the calling line number and
+   terminates. */
+#define check(EXPR) check_func ((EXPR), __LINE__)
+\f
+/* Node type and support routines. */
+
+/* Test data block. */
+struct block
+  {
+    struct tower_node node;     /* Embedded tower block. */
+    int x;                      /* Primary value. */
+  };
+
+/* Returns the `struct block' that NODE is embedded within. */
+static struct block *
+tower_node_to_block (const struct tower_node *node)
+{
+  return tower_data (node, struct block, node);
+}
+
+/* Swaps *A and *B. */
+static void
+swap (int *a, int *b) 
+{
+  int t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Reverses the order of the CNT integers starting at VALUES. */
+static void
+reverse (int *values, size_t cnt)
+{
+  size_t i = 0;
+  size_t j = cnt;
+
+  while (j > i)
+    swap (&values[i++], &values[--j]);
+}
+
+/* Arranges the CNT blocks in VALUES into the lexicographically
+   next greater permutation.  Returns true if successful.
+   If VALUES is already the lexicographically greatest
+   permutation of its blocks (i.e. ordered from greatest to
+   smallest), arranges them into the lexicographically least
+   permutation (i.e. ordered from smallest to largest) and
+   returns false. */
+static bool
+next_permutation (int *values, size_t cnt)
+{
+  if (cnt > 0)
+    {
+      size_t i = cnt - 1;
+      while (i != 0) 
+        {
+          i--;
+          if (values[i] < values[i + 1])
+            {
+              size_t j;
+              for (j = cnt - 1; values[i] >= values[j]; j--)
+                continue;
+              swap (values + i, values + j);
+              reverse (values + (i + 1), cnt - (i + 1));
+              return true;
+            } 
+        }
+      
+      reverse (values, cnt);
+    }
+  
+  return false;
+}
+
+/* Returns N!. */
+static unsigned int
+factorial (unsigned int n) 
+{
+  unsigned int value = 1;
+  /* Disallow N values that overflow on 32-bit machines. */
+  assert (n <= 12);
+  for (; n > 1; )
+    value *= n--;
+  return value;
+}
+
+/* Returns C(n, k), the number of ways that K choices can be made
+   from N items when order is unimportant. */
+static unsigned int
+binomial_cofficient (unsigned int n, unsigned int k) 
+{
+  assert (n >= k);
+  return factorial (n) / factorial (k) / factorial (n - k);
+}
+
+/* Tests whether PARTS is a K-part integer composition of N.
+   Returns true if so, false otherwise. */
+static bool UNUSED
+is_k_composition (int n, int k, const int parts[]) 
+{
+  int sum;
+  int i;
+
+  sum = 0;
+  for (i = 0; i < k; i++)
+    {
+      if (parts[i] < 1 || parts[i] > n)
+        return false;
+      sum += parts[i];
+    }
+  return sum == n;
+}
+
+/* Advances the K-part integer composition of N stored in PARTS
+   to the next lexicographically greater one.
+   Returns true if successful, false if the composition was
+   already the greatest K-part composition of N (in which case
+   PARTS is unaltered). */
+static bool
+next_k_composition (int n UNUSED, int k, int parts[]) 
+{
+  int x, i;
+
+  assert (is_k_composition (n, k, parts));
+  if (k == 1)
+    return false;
+
+  for (i = k - 1; i > 0; i--)
+    if (parts[i] > 1)
+      break;
+  if (i == 0)
+    return false;
+
+  x = parts[i] - 1;
+  parts[i] = 1;
+  parts[i - 1]++;
+  parts[k - 1] = x;
+
+  assert (is_k_composition (n, k, parts));
+  return true;
+}
+
+/* Sets the K integers in PARTS to the lexicographically first
+   K-part composition of N. */
+static void
+first_k_composition (int n, int k, int parts[]) 
+{
+  int i;
+
+  assert (n >= k);
+
+  for (i = 0; i < k; i++)
+    parts[i] = 1;
+  parts[k - 1] += n - k;
+}
+
+/* Advances *K and PARTS to the next integer composition of N.
+   Compositions are ordered from shortest to longest and in
+   lexicographical order within a given length.
+   Before the first call, initialize *K to 0.
+   After each successful call, *K contains the length of the
+   current composition and the *K blocks in PARTS contain its
+   parts.
+   Returns true if successful, false if the set of compositions
+   has been exhausted. */
+static bool
+next_composition (int n, int *k, int parts[]) 
+{
+  if (*k >= 1 && next_k_composition (n, *k, parts))
+    return true;
+  else if (*k < n)
+    {
+      first_k_composition (n, ++*k, parts);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* A block expected to be found in a tower. */
+struct expected_block 
+  {
+    int height;         /* Expected height of bottom of block. */
+    int x;              /* Expected value for `x' member. */
+  };
+
+/* Checks that tower T contains the BLOCK_CNT blocks described by
+   BLOCKS[]. */
+static void
+check_tower (struct tower *t,
+             struct expected_block blocks[], size_t block_cnt) 
+{
+  int total_height;
+  struct tower_node *node;
+  size_t i;
+  
+  check (tower_is_empty (t) == (block_cnt == 0));
+
+  total_height = 0;
+  for (i = 0; i < block_cnt; i++) 
+    {
+      unsigned long int level;
+      for (level = total_height;
+           level < total_height + blocks[i].height;
+           level++) 
+        {
+          struct tower_node *found;
+          unsigned long int block_start;
+          found = tower_lookup (t, level, &block_start);
+          check (found != NULL);
+          check (tower_node_to_block (found)->x == blocks[i].x);
+          check (block_start == total_height);
+        }
+      total_height += blocks[i].height; 
+    }
+  check (tower_height (t) == total_height);
+
+  for (node = tower_first (t), i = 0;
+       node != NULL;
+       node = tower_next (t, node), i++) 
+    {
+      check (tower_node_get_height (node) == blocks[i].height);
+      check (tower_node_to_block (node)->x == blocks[i].x);
+    }
+  check (i == block_cnt);
+}
+\f
+/* Tests inserting all possible sets of block heights into a
+   tower in all possible orders, up to a specified maximum tower
+   height. */
+static void
+test_insert (void) 
+{
+  const int max_height = 7;
+  int cnt;
+
+  for (cnt = 1; cnt <= max_height; cnt++) 
+    {
+      unsigned int composition_cnt;
+      struct expected_block *expected;
+      int *heights;
+      int block_cnt;
+      int *order;
+      struct block *blocks;
+      
+      expected = xnmalloc (cnt, sizeof *expected);
+      heights = xnmalloc (cnt, sizeof *heights);
+      order = xnmalloc (cnt, sizeof *order);
+      blocks = xnmalloc (cnt, sizeof *blocks);
+
+      block_cnt = 0;
+      composition_cnt = 0;
+      while (next_composition (cnt, &block_cnt, heights)) 
+        {
+          int i, j;
+          unsigned int permutation_cnt;
+
+          for (i = 0; i < block_cnt; i++) 
+            order[i] = i;
+
+          permutation_cnt = 0;
+          while (permutation_cnt == 0 || next_permutation (order, block_cnt)) 
+            {
+              struct tower t;
+
+              /* Inserts the block_cnt blocks with the given
+                 heights[] into T in the order given by order[]. */
+              tower_init (&t);
+              for (i = 0; i < block_cnt; i++) 
+                {
+                  struct block *under;
+                  int idx;
+
+                  idx = order[i];
+                  blocks[idx].x = idx;
+
+                  under = NULL;
+                  for (j = 0; j < i; j++)
+                    if (idx < order[j]
+                        && (under == NULL || under->x > order[j]))
+                      under = &blocks[order[j]];
+
+                  tower_insert (&t, heights[idx], &blocks[idx].node,
+                                under != NULL ? &under->node : NULL);
+                }
+
+              /* Check that the result is what we expect. */
+              for (i = 0; i < block_cnt; i++)
+                {
+                  expected[i].height = heights[i];
+                  expected[i].x = i;
+                }
+              check_tower (&t, expected, block_cnt);
+
+              permutation_cnt++;
+            }
+          check (permutation_cnt == factorial (block_cnt));
+          
+          composition_cnt++;
+        }
+      check (composition_cnt == 1 << (cnt - 1));
+
+      free (expected);
+      free (heights);
+      free (order);
+      free (blocks);
+    }
+}
+
+/* Tests deleting blocks from towers that initially contain all
+   possible sets of block heights into a tower in all possible
+   orders, up to a specified maximum tower height. */
+static void
+test_delete (void) 
+{
+  const int max_height = 7;
+  int cnt;
+
+  for (cnt = 1; cnt <= max_height; cnt++) 
+    {
+      unsigned int composition_cnt;
+      struct expected_block *expected;
+      int *heights;
+      int block_cnt;
+      int *order;
+      struct block *blocks;
+      
+      expected = xnmalloc (cnt, sizeof *expected);
+      heights = xnmalloc (cnt, sizeof *heights);
+      order = xnmalloc (cnt, sizeof *order);
+      blocks = xnmalloc (cnt, sizeof *blocks);
+
+      block_cnt = 0;
+      composition_cnt = 0;
+      while (next_composition (cnt, &block_cnt, heights)) 
+        {
+          int i;
+          unsigned int permutation_cnt;
+
+          for (i = 0; i < block_cnt; i++) 
+            order[i] = i;
+
+          permutation_cnt = 0;
+          while (permutation_cnt == 0 || next_permutation (order, block_cnt)) 
+            {
+              struct tower t;
+
+              /* Insert blocks into tower in ascending order. */
+              tower_init (&t);
+              for (i = 0; i < block_cnt; i++) 
+                {
+                  blocks[i].x = i;
+                  tower_insert (&t, heights[i], &blocks[i].node, NULL);
+                  expected[i].x = i;
+                  expected[i].height = heights[i];
+                }
+              check_tower (&t, expected, block_cnt);
+              
+              /* Delete blocks from tower in the order of
+                 order[]. */
+              for (i = 0; i < block_cnt; i++)
+                {
+                  int idx = order[i];
+                  int j;
+                  tower_delete (&t, &blocks[idx].node);
+                  for (j = 0; ; j++) 
+                    {
+                      assert (j < block_cnt - i);
+                      if (expected[j].x == idx)
+                        {
+                          memcpy (&expected[j], &expected[j + 1],
+                                  sizeof *expected * (block_cnt - i - j - 1));
+                          break;
+                        }
+                    }
+                  check_tower (&t, expected, block_cnt - i - 1);
+                }
+
+              permutation_cnt++;
+            }
+          check (permutation_cnt == factorial (block_cnt));
+          
+          composition_cnt++;
+        }
+      check (composition_cnt == 1 << (cnt - 1));
+
+      free (expected);
+      free (heights);
+      free (order);
+      free (blocks);
+    }
+}
+
+/* Tests towers containing all possible block heights, resizing
+   the blocks to all possible heights that conserve the total
+   tower height, up to a maximum total tower height. */
+static void
+test_resize (void) 
+{
+  const int max_height = 9;
+  int cnt;
+
+  for (cnt = 1; cnt <= max_height; cnt++) 
+    {
+      unsigned int composition_cnt;
+      struct expected_block *expected;
+      int *heights, *new_heights;
+      int block_cnt;
+      int *order;
+      struct block *blocks;
+      
+      expected = xnmalloc (cnt, sizeof *expected);
+      heights = xnmalloc (cnt, sizeof *heights);
+      new_heights = xnmalloc (cnt, sizeof *new_heights);
+      order = xnmalloc (cnt, sizeof *order);
+      blocks = xnmalloc (cnt, sizeof *blocks);
+
+      block_cnt = 0;
+      composition_cnt = 0;
+      while (next_composition (cnt, &block_cnt, heights)) 
+        {
+          int i;
+          unsigned int resizes = 0;
+
+          for (resizes = 0, first_k_composition (cnt, block_cnt, new_heights);
+               (resizes == 0
+                || next_k_composition (cnt, block_cnt, new_heights));
+               resizes++)
+            {
+              struct tower t;
+
+              /* Insert blocks into tower in ascending order. */
+              tower_init (&t);
+              for (i = 0; i < block_cnt; i++) 
+                {
+                  blocks[i].x = i;
+                  tower_insert (&t, heights[i], &blocks[i].node, NULL);
+                  expected[i].x = i;
+                  expected[i].height = heights[i];
+                }
+              check_tower (&t, expected, block_cnt);
+
+              /* Resize all the blocks. */
+              for (i = 0; i < block_cnt; i++) 
+                {
+                  if (expected[i].height != new_heights[i] || rand () % 2)
+                    tower_resize (&t, &blocks[i].node, new_heights[i]);
+                  expected[i].height = new_heights[i];
+                }
+              check_tower (&t, expected, block_cnt);
+            }
+          check (resizes == binomial_cofficient (cnt - 1, block_cnt - 1));
+          
+          composition_cnt++;
+        }
+      check (composition_cnt == 1 << (cnt - 1));
+
+      free (expected);
+      free (new_heights);
+      free (heights);
+      free (order);
+      free (blocks);
+    }
+}
+
+/* Tests splicing all possible contiguous sets of blocks out of one
+   tower into a second, initially empty tower. */
+static void
+test_splice_out (void) 
+{
+  const int max_height = 9;
+  int cnt;
+
+  for (cnt = 1; cnt <= max_height; cnt++) 
+    {
+      unsigned int composition_cnt;
+      struct expected_block *expected;
+      int *heights, *new_heights;
+      int block_cnt;
+      int *order;
+      struct block *blocks;
+      
+      expected = xnmalloc (cnt, sizeof *expected);
+      heights = xnmalloc (cnt, sizeof *heights);
+      new_heights = xnmalloc (cnt, sizeof *new_heights);
+      order = xnmalloc (cnt, sizeof *order);
+      blocks = xnmalloc (cnt, sizeof *blocks);
+
+      block_cnt = 0;
+      composition_cnt = 0;
+      while (next_composition (cnt, &block_cnt, heights)) 
+        {
+          int i, j;
+
+          for (i = 0; i < block_cnt; i++)
+            for (j = i; j <= block_cnt; j++)
+              {
+                struct tower src, dst;
+                int k;
+
+                tower_init (&src);
+                tower_init (&dst);
+
+                /* Insert blocks into SRC and DST in ascending order. */
+                for (k = 0; k < block_cnt; k++) 
+                  {
+                    blocks[k].x = k;
+                    tower_insert (&src, heights[k], &blocks[k].node, NULL);
+                    expected[k].x = k;
+                    expected[k].height = heights[k];
+                  }
+                check_tower (&src, expected, block_cnt);
+
+                /* Splice blocks I...J into DST. */
+                tower_splice (&dst, NULL, &src, &blocks[i].node,
+                              j < block_cnt ? &blocks[j].node : NULL);
+                check_tower (&dst, &expected[i], j - i);
+                memmove (&expected[i], &expected[j],
+                         sizeof *expected * (block_cnt - j));
+                check_tower (&src, expected, block_cnt - (j - i));
+              }
+           composition_cnt++;
+        }
+      check (composition_cnt == 1 << (cnt - 1));
+
+      free (expected);
+      free (new_heights);
+      free (heights);
+      free (order);
+      free (blocks);
+    }
+}
+
+/* Tests splicing all of the contents of a tower into all
+   possible positions in a second tower. */
+static void
+test_splice_in (void) 
+{
+  const int max_height = 9;
+  int cnt;
+
+  for (cnt = 1; cnt <= max_height; cnt++) 
+    {
+      unsigned int composition_cnt;
+      struct expected_block *expected;
+      int *heights, *new_heights;
+      int block_cnt;
+      int *order;
+      struct block *blocks;
+      
+      expected = xnmalloc (cnt, sizeof *expected);
+      heights = xnmalloc (cnt, sizeof *heights);
+      new_heights = xnmalloc (cnt, sizeof *new_heights);
+      order = xnmalloc (cnt, sizeof *order);
+      blocks = xnmalloc (cnt, sizeof *blocks);
+
+      block_cnt = 0;
+      composition_cnt = 0;
+      while (next_composition (cnt, &block_cnt, heights)) 
+        {
+          int i, j;
+
+          for (i = 0; i < block_cnt; i++)
+            for (j = i; j <= block_cnt; j++)
+              {
+                struct tower src, dst;
+                int k;
+
+                tower_init (&src);
+                tower_init (&dst);
+
+                /* Insert blocks into SRC and DST in ascending order. */
+                for (k = 0; k < block_cnt; k++) 
+                  {
+                    blocks[k].x = k;
+                    tower_insert (k >= i && k < j ? &src : &dst, 
+                                  heights[k], &blocks[k].node, NULL);
+                    expected[k].x = k;
+                    expected[k].height = heights[k];
+                  }
+
+                /* Splice SRC into DST. */
+                tower_splice (&dst, j < block_cnt ? &blocks[j].node : NULL,
+                              &src, i != j ? &blocks[i].node : NULL, NULL);
+                check_tower (&dst, expected, block_cnt);
+              }
+           composition_cnt++;
+        }
+      check (composition_cnt == 1 << (cnt - 1));
+
+      free (expected);
+      free (new_heights);
+      free (heights);
+      free (order);
+      free (blocks);
+    }
+}
+
+\f
+/* Main program. */
+
+/* Runs TEST_FUNCTION and prints a message about NAME. */
+static void
+run_test (void (*test_function) (void), const char *name) 
+{
+  test_name = name;
+  putchar ('.');
+  fflush (stdout);
+  test_function ();
+}
+
+int
+main (void) 
+{
+  run_test (test_insert, "insert");
+  run_test (test_delete, "delete");
+  run_test (test_resize, "resize");
+  run_test (test_splice_out, "splice out");
+  run_test (test_splice_in, "splice in");
+  putchar ('\n');
+
+  return 0;
+}