1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* This is a test program for the routines defined in
18 range-map.c. This test program aims to be as comprehensive as
19 possible. With -DNDEBUG, "gcov -b" should report 100%
20 coverage of lines and branches in range-map.c routines.
21 (Without -DNDEBUG, branches caused by failed assertions will
22 not be taken.) "valgrind --leak-check=yes
23 --show-reachable=yes" should give a clean report, both with
24 and without -DNDEBUG. */
30 #include <libpspp/range-map.h>
38 #include <libpspp/compiler.h>
42 /* Exit with a failure code.
43 (Place a breakpoint on this function while debugging.) */
50 /* If OK is not true, prints a message about failure on the
51 current source file and the given LINE and terminates. */
53 check_func (bool ok, int line)
57 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
62 /* Verifies that EXPR evaluates to true.
63 If not, prints a message citing the calling line number and
65 #define check(EXPR) check_func ((EXPR), __LINE__)
67 /* Swaps *A and *B. */
76 /* Reverses the order of the CNT integers starting at VALUES. */
78 reverse (int *values, size_t cnt)
84 swap (&values[i++], &values[--j]);
87 /* Arranges the CNT blocks in VALUES into the lexicographically
88 next greater permutation. Returns true if successful.
89 If VALUES is already the lexicographically greatest
90 permutation of its blocks (i.e. ordered from greatest to
91 smallest), arranges them into the lexicographically least
92 permutation (i.e. ordered from smallest to largest) and
95 next_permutation (int *values, size_t cnt)
103 if (values[i] < values[i + 1])
106 for (j = cnt - 1; values[i] >= values[j]; j--)
108 swap (values + i, values + j);
109 reverse (values + (i + 1), cnt - (i + 1));
114 reverse (values, cnt);
122 factorial (unsigned int n)
124 unsigned int value = 1;
125 /* Disallow N values that overflow on 32-bit machines. */
132 /* Tests whether PARTS is a K-part integer composition of N.
133 Returns true if so, false otherwise. */
135 is_k_composition (int n, int k, const int parts[])
141 for (i = 0; i < k; i++)
143 if (parts[i] < 1 || parts[i] > n)
150 /* Advances the K-part integer composition of N stored in PARTS
151 to the next lexicographically greater one.
152 Returns true if successful, false if the composition was
153 already the greatest K-part composition of N (in which case
154 PARTS is unaltered). */
156 next_k_composition (int n UNUSED, int k, int parts[])
160 assert (is_k_composition (n, k, parts));
164 for (i = k - 1; i > 0; i--)
175 assert (is_k_composition (n, k, parts));
179 /* Sets the K integers in PARTS to the lexicographically first
180 K-part composition of N. */
182 first_k_composition (int n, int k, int parts[])
188 for (i = 0; i < k; i++)
190 parts[k - 1] += n - k;
193 /* Advances *K and PARTS to the next integer composition of N.
194 Compositions are ordered from shortest to longest and in
195 lexicographical order within a given length.
196 Before the first call, initialize *K to 0.
197 After each successful call, *K contains the length of the
198 current composition and the *K blocks in PARTS contain its
200 Returns true if successful, false if the set of compositions
201 has been exhausted. */
203 next_composition (int n, int *k, int parts[])
205 if (*k >= 1 && next_k_composition (n, *k, parts))
209 first_k_composition (n, ++*k, parts);
216 /* Test data element. */
219 struct range_map_node node; /* Embedded tower block. */
220 int x; /* Primary value. */
223 static struct element *
224 range_map_node_to_element (struct range_map_node *node)
226 return range_map_data (node, struct element, node);
229 /* Element we expect to find. */
230 struct expected_element
232 int x; /* Primary value. */
233 unsigned long int start; /* Start of region. */
234 unsigned long int end; /* End of region. */
237 /* Compares expected_element A and B and returns a strcmp()-type
240 compare_expected_element (const void *a_, const void *b_)
242 const struct expected_element *a = (const struct expected_element *) a_;
243 const struct expected_element *b = (const struct expected_element *) b_;
244 return a->start < b->start ? -1 : a->start > b->start;
247 /* Checks that RM contains the ELEM_CNT elements described by
250 check_range_map (struct range_map *rm,
251 struct expected_element elements[], size_t elem_cnt)
253 struct expected_element *sorted;
254 struct range_map_node *node;
257 sorted = xnmalloc (elem_cnt, sizeof *sorted);
258 memcpy (sorted, elements, elem_cnt * sizeof *elements);
259 qsort (sorted, elem_cnt, sizeof *sorted, compare_expected_element);
261 check (range_map_is_empty (rm) == (elem_cnt == 0));
263 for (i = 0; i < elem_cnt; i++)
265 struct expected_element *e = &sorted[i];
266 unsigned long int position;
268 /* Check that range_map_lookup finds all the positions
269 within the element. */
270 for (position = e->start; position < e->end; position++)
272 struct range_map_node *found = range_map_lookup (rm, position);
273 check (found != NULL);
274 check (range_map_node_to_element (found)->x == e->x);
275 check (range_map_node_get_start (found) == e->start);
276 check (range_map_node_get_end (found) == e->end);
277 check (range_map_node_get_width (found) == e->end - e->start);
280 /* If there shouldn't be any elements in the positions just
281 before or after the element, verify that
282 range_map_lookup doesn't find any there. */
283 if (e->start > 0 && (i == 0 || e[-1].end < e->start))
284 check (range_map_lookup (rm, e->start - 1) == NULL);
285 if (i == elem_cnt - 1 || e->end < e[1].start)
286 check (range_map_lookup (rm, e->end) == NULL);
289 for (node = (rand () % 2 ? range_map_first (rm) : range_map_next (rm, NULL)),
292 node = range_map_next (rm, node), i++)
294 struct expected_element *e = &sorted[i];
295 check (range_map_node_to_element (node)->x == e->x);
297 check (i == elem_cnt);
302 /* Tests inserting all possible sets of ranges into a range map
303 in all possible orders, up to a specified maximum overall
308 const int max_range = 7;
311 for (cnt = 1; cnt <= max_range; cnt++)
313 unsigned int composition_cnt;
314 struct expected_element *expected;
318 struct element *elements;
320 expected = xnmalloc (cnt, sizeof *expected);
321 widths = xnmalloc (cnt, sizeof *widths);
322 order = xnmalloc (cnt, sizeof *order);
323 elements = xnmalloc (cnt, sizeof *elements);
327 while (next_composition (cnt, &elem_cnt, widths))
330 unsigned int permutation_cnt;
332 for (i = 0; i < elem_cnt; i++)
336 while (permutation_cnt == 0 || next_permutation (order, elem_cnt))
340 /* Inserts the elem_cnt elements with the given
341 widths[] into T in the order given by order[]. */
342 range_map_init (&rm);
343 for (i = 0; i < elem_cnt; i++)
345 unsigned long int start, end;
349 elements[idx].x = idx;
351 /* Find start and end of element. */
353 for (j = 0; j < idx; j++)
355 end = start + widths[j];
358 range_map_insert (&rm, start, end - start,
359 &elements[idx].node);
361 /* Check map contents. */
363 expected[i].start = start;
364 expected[i].end = end;
365 check_range_map (&rm, expected, i + 1);
369 check (permutation_cnt == factorial (elem_cnt));
373 check (composition_cnt == 1 << (cnt - 1));
382 /* Tests deleting ranges from a range map in all possible orders,
383 up to a specified maximum overall range. */
385 test_delete (int gap)
387 const int max_range = 7;
390 for (cnt = 1; cnt <= max_range; cnt++)
392 unsigned int composition_cnt;
393 struct expected_element *expected;
397 struct element *elements;
399 expected = xnmalloc (cnt, sizeof *expected);
400 widths = xnmalloc (cnt, sizeof *widths);
401 order = xnmalloc (cnt, sizeof *order);
402 elements = xnmalloc (cnt, sizeof *elements);
406 while (next_composition (cnt, &elem_cnt, widths))
409 unsigned int permutation_cnt;
411 for (i = 0; i < elem_cnt; i++)
415 while (permutation_cnt == 0 || next_permutation (order, elem_cnt))
418 unsigned long int start;
420 /* Insert all the elements. */
421 range_map_init (&rm);
423 for (i = 0; i < elem_cnt; i++)
425 int width = widths[i] > gap ? widths[i] - gap : widths[i];
426 unsigned long int end = start + width;
429 range_map_insert (&rm, start, end - start,
434 assert (j < elem_cnt);
438 expected[j].start = start;
439 expected[j].end = end;
446 check_range_map (&rm, expected, elem_cnt);
448 /* Delete the elements in the specified order. */
449 for (i = 0; i < elem_cnt; i++)
451 range_map_delete (&rm, &elements[order[i]].node);
452 check_range_map (&rm, expected + i + 1, elem_cnt - i - 1);
457 check (permutation_cnt == factorial (elem_cnt));
461 check (composition_cnt == 1 << (cnt - 1));
470 /* Tests deleting ranges from a range map filled with contiguous
471 ranges in all possible orders, up to a specified maximum
474 test_delete_contiguous (void)
479 /* Tests deleting ranges from a range map filled with ranges
480 sometimes separated by gaps in all possible orders, up to a
481 specified maximum overall range. */
483 test_delete_gaps (void)
493 const char *description;
494 void (*function) (void);
497 static const struct test tests[] =
506 "delete from contiguous ranges",
507 test_delete_contiguous
511 "delete from ranges separated by gaps",
516 enum { N_TESTS = sizeof tests / sizeof *tests };
519 main (int argc, char *argv[])
525 fprintf (stderr, "exactly one argument required; use --help for help\n");
528 else if (!strcmp (argv[1], "--help"))
530 printf ("%s: test range map library\n"
531 "usage: %s TEST-NAME\n"
532 "where TEST-NAME is one of the following:\n",
534 for (i = 0; i < N_TESTS; i++)
535 printf (" %s\n %s\n", tests[i].name, tests[i].description);
540 for (i = 0; i < N_TESTS; i++)
541 if (!strcmp (argv[1], tests[i].name))
543 tests[i].function ();
547 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);