range-set: Add new function range_set_scan().
[pspp-builds.git] / tests / libpspp / range-set-test.c
index be09d884db809beb5fb0d0e5e58dfa550c78de0d..2706b70d23ecf00a8dc30ea639580f8edcef01f1 100644 (file)
@@ -1,20 +1,18 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2007, 2009 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 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 3 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.
+   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. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 /* This is a test program for the routines defined in
    range-set.c.  This test program aims to be as comprehensive as
@@ -121,6 +119,18 @@ next_region (unsigned int pattern, unsigned int offset,
   return false;
 }
 
+/* Searches the bits in PATTERN from right to left starting from
+   bit OFFSET.  Returns the bit index of the first 1-bit found,
+   or ULONG_MAX if none is found. */
+static unsigned long int
+next_1bit (unsigned int pattern, unsigned int offset)
+{
+  for (; offset < UINT_BIT; offset++)
+    if (pattern & (1u << offset))
+      return offset;
+  return ULONG_MAX;
+}
+
 /* Prints the regions in RS to stdout. */
 static void UNUSED
 print_regions (const struct range_set *rs)
@@ -141,6 +151,7 @@ check_pattern (const struct range_set *rs, unsigned int pattern)
 {
   const struct range_set_node *node;
   unsigned long int start, width;
+  unsigned long int s1, s2;
   int i;
 
   for (node = rand () % 2 ? range_set_first (rs) : range_set_next (rs, NULL),
@@ -155,6 +166,40 @@ check_pattern (const struct range_set *rs, unsigned int pattern)
     }
   check (node == NULL);
 
+  /* Scan from all possible positions, resetting the cache each
+     time, to ensure that we get the correct answers without
+     caching. */
+  for (start = 0; start <= 32; start++)
+    {
+      struct range_set *nonconst_rs = (struct range_set *) rs;
+      nonconst_rs->cache_end = 0;
+      s1 = range_set_scan (rs, start);
+      s2 = next_1bit (pattern, start);
+      check (s1 == s2);
+    }
+
+  /* Scan in forward order to exercise expected cache behavior. */
+  for (s1 = range_set_scan (rs, 0), s2 = next_1bit (pattern, 0); ;
+       s1 = range_set_scan (rs, s1 + 1), s2 = next_1bit (pattern, s2 + 1))
+    {
+      check (s1 == s2);
+      if (s1 == ULONG_MAX)
+        break;
+    }
+
+  /* Scan in random order to frustrate cache. */
+  for (i = 0; i < 32; i++)
+    {
+      start = rand () % 32;
+      s1 = range_set_scan (rs, start);
+      s2 = next_1bit (pattern, start);
+      check (s1 == s2);
+    }
+
+  /* Test range_set_scan() with negative cache. */
+  check (!range_set_contains (rs, 999));
+  check (range_set_scan (rs, 1111) == ULONG_MAX);
+
   for (i = 0; i < UINT_BIT; i++)
     check (range_set_contains (rs, i) == ((pattern & (1u << i)) != 0));
   check (!range_set_contains (rs,
@@ -290,6 +335,60 @@ test_allocate (void)
       }
 }
 
+/* Tests all possible full allocations in all possible range sets
+   (up to a small maximum number of bits). */
+static void
+test_allocate_fully (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, expect_start;
+        bool success, expect_success;
+        unsigned int final_pat;
+        int i;
+
+        /* Figure out expected results. */
+        expect_success = false;
+        expect_start = 0;
+        final_pat = init_pat;
+        for (i = 0; i < positions - request + 1; i++)
+          {
+            int j;
+
+            final_pat = init_pat;
+            for (j = i; j < i + request; j++)
+              {
+                if (!(init_pat & (1u << j)))
+                  goto next;
+                final_pat &= ~(1u << j);
+              }
+
+            expect_success = true;
+            expect_start = i;
+            break;
+          next:
+            final_pat = init_pat;
+          }
+
+        /* Test. */
+        rs = make_pattern (init_pat);
+        success = range_set_allocate_fully (rs, request, &start);
+        check_pattern (rs, final_pat);
+        range_set_destroy (rs);
+
+        /* Check results. */
+        check (success == expect_success);
+        if (expect_success)
+          check (start == expect_start);
+      }
+}
+
 /* Tests freeing a range set through a pool. */
 static void
 test_pool (void)
@@ -312,6 +411,13 @@ test_pool (void)
   range_set_insert (rs, 1, 10);
   pool_destroy (pool);
 }
+
+/* Tests range_set_destroy(NULL). */
+static void
+test_destroy_null (void)
+{
+  range_set_destroy (NULL);
+}
 \f
 /* Main program. */
 
@@ -331,7 +437,9 @@ main (void)
   run_test (test_insert, "insert");
   run_test (test_delete, "delete");
   run_test (test_allocate, "allocate");
+  run_test (test_allocate_fully, "allocate_fully");
   run_test (test_pool, "pool");
+  run_test (test_destroy_null, "destroy null");
   putchar ('\n');
 
   return 0;