1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2010, 2012 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 heap.c.
18 This test program aims to be as comprehensive as possible.
19 With -DNDEBUG, "gcov -b" should report 100% coverage of lines
20 and branches in heap.c routines, except for the is_heap
21 function, which is not called at all with -DNDEBUG. (Without
22 -DNDEBUG, branches caused by failed assertions will also not
23 be taken.) "valgrind --leak-check=yes --show-reachable=yes"
24 should give a clean report, both with and without -DNDEBUG. */
30 #include <libpspp/heap.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 /* Node type and support routines. */
69 /* Test data element. */
72 struct heap_node node; /* Embedded heap element. */
73 int x; /* Primary value. */
78 /* Returns the `struct element' that NODE is embedded within. */
79 static struct element *
80 heap_node_to_element (const struct heap_node *node)
82 return heap_data (node, struct element, node);
85 /* Compares the `x' values in A and B and returns a strcmp-type
86 return value. Verifies that AUX points to aux_data. */
88 compare_elements (const struct heap_node *a_, const struct heap_node *b_,
91 const struct element *a = heap_node_to_element (a_);
92 const struct element *b = heap_node_to_element (b_);
94 check (aux == &aux_data);
95 return a->x < b->x ? -1 : a->x > b->x;
98 /* Returns the smallest of the N integers in ARRAY. */
100 min_int (int *array, size_t n)
106 for (i = 0; i < n; i++)
112 /* Swaps *A and *B. */
114 swap (int *a, int *b)
121 /* Reverses the order of the CNT integers starting at VALUES. */
123 reverse (int *values, size_t cnt)
129 swap (&values[i++], &values[--j]);
132 /* Arranges the CNT elements in VALUES into the lexicographically
133 next greater permutation. Returns true if successful.
134 If VALUES is already the lexicographically greatest
135 permutation of its elements (i.e. ordered from greatest to
136 smallest), arranges them into the lexicographically least
137 permutation (i.e. ordered from smallest to largest) and
140 next_permutation (int *values, size_t cnt)
148 if (values[i] < values[i + 1])
151 for (j = cnt - 1; values[i] >= values[j]; j--)
153 swap (values + i, values + j);
154 reverse (values + (i + 1), cnt - (i + 1));
159 reverse (values, cnt);
167 factorial (unsigned int n)
169 unsigned int value = 1;
175 /* Returns the number of permutations of the CNT values in
176 VALUES. If VALUES contains duplicates, they must be
179 expected_perms (int *values, size_t cnt)
182 unsigned int perm_cnt;
184 perm_cnt = factorial (cnt);
185 for (i = 0; i < cnt; i = j)
187 for (j = i + 1; j < cnt; j++)
188 if (values[i] != values[j])
190 perm_cnt /= factorial (j - i);
195 /* Tests whether PARTS is a K-part integer composition of N.
196 Returns true if so, false otherwise. */
198 is_k_composition (int n, int k, const int parts[])
204 for (i = 0; i < k; i++)
206 if (parts[i] < 1 || parts[i] > n)
213 /* Advances the K-part integer composition of N stored in PARTS
214 to the next lexicographically greater one.
215 Returns true if successful, false if the composition was
216 already the greatest K-part composition of N (in which case
217 PARTS is unaltered). */
219 next_k_composition (int n UNUSED, int k, int parts[])
223 assert (is_k_composition (n, k, parts));
227 for (i = k - 1; i > 0; i--)
238 assert (is_k_composition (n, k, parts));
242 /* Advances *K and PARTS to the next integer composition of N.
243 Compositions are ordered from shortest to longest and in
244 lexicographical order within a given length.
245 Before the first call, initialize *K to 0.
246 After each successful call, *K contains the length of the
247 current composition and the *K elements in PARTS contain its
249 Returns true if successful, false if the set of compositions
250 has been exhausted. */
252 next_composition (int n, int *k, int parts[])
254 if (*k >= 1 && next_k_composition (n, *k, parts))
259 for (i = 0; i < *k; i++)
269 /* Inserts sequences without duplicates into a heap, and then
270 ensures that they appear as the minimum element in the correct
271 order as we delete them. Exhaustively tests every input
272 permutation up to 'max_elems' elements. */
274 test_insert_no_dups_delete_min (void)
276 const int max_elems = 8;
279 for (cnt = 0; cnt <= max_elems; cnt++)
282 struct element *elements;
284 unsigned int permutation_cnt;
287 values = xnmalloc (cnt, sizeof *values);
288 elements = xnmalloc (cnt, sizeof *elements);
289 for (i = 0; i < cnt; i++)
292 h = heap_create (compare_elements, &aux_data);
294 while (permutation_cnt == 0 || next_permutation (values, cnt))
298 for (i = 0; i < cnt; i++)
299 elements[i].x = values[i];
301 check (heap_is_empty (h));
302 for (i = 0; i < cnt; i++)
304 heap_insert (h, &elements[i].node);
305 check (heap_node_to_element (heap_minimum (h))->x
306 == min_int (values, i + 1));
307 check (heap_count (h) == i + 1);
310 for (i = 0; i < cnt; i++)
312 check (heap_node_to_element (heap_minimum (h))->x == i);
313 heap_delete (h, heap_minimum (h));
315 check (heap_is_empty (h));
318 check (permutation_cnt == factorial (cnt));
325 /* Inserts sequences with duplicates into a heap, and then
326 ensures that they appear as the minimum element in the correct
327 order as we delete them. Exhaustively tests every input
328 permutation up to 'max_elems' elements.
330 See Usenet article <87mz4utika.fsf@blp.benpfaff.org> for
331 details of the algorithm used here. */
333 test_insert_with_dups_delete_min (void)
335 const int max_elems = 7;
338 for (cnt = 1; cnt <= max_elems; cnt++)
340 unsigned int composition_cnt;
345 struct element *elements;
348 dups = xnmalloc (cnt, sizeof *dups);
349 values = xnmalloc (cnt, sizeof *values);
350 sorted_values = xnmalloc (cnt, sizeof *sorted_values);
351 elements = xnmalloc (cnt, sizeof *elements);
355 while (next_composition (cnt, &unique_cnt, dups))
359 unsigned int permutation_cnt;
362 for (i = 0; i < unique_cnt; i++)
363 for (j = 0; j < dups[i]; j++)
366 sorted_values[k] = i;
371 h = heap_create (compare_elements, &aux_data);
373 while (permutation_cnt == 0 || next_permutation (values, cnt))
377 for (i = 0; i < cnt; i++)
378 elements[i].x = values[i];
381 check (heap_is_empty (h));
382 for (i = 0; i < cnt; i++)
384 heap_insert (h, &elements[i].node);
387 check (heap_node_to_element (heap_minimum (h))->x == min);
388 check (heap_count (h) == i + 1);
391 for (i = 0; i < cnt; i++)
393 struct element *min = heap_node_to_element (heap_minimum (h));
394 check (min->x == sorted_values[i]);
395 heap_delete (h, heap_minimum (h));
397 check (heap_is_empty (h));
400 check (permutation_cnt == expected_perms (values, cnt));
405 check (composition_cnt == 1 << (cnt - 1));
409 free (sorted_values);
414 /* Inserts a sequence without duplicates into a heap, then
415 deletes them in a different order. */
417 test_insert_no_dups_delete_random (void)
419 const int max_elems = 5;
422 for (cnt = 0; cnt <= max_elems; cnt++)
425 struct element *elements;
426 int *insert, *delete;
427 unsigned int insert_perm_cnt;
430 insert = xnmalloc (cnt, sizeof *insert);
431 delete = xnmalloc (cnt, sizeof *delete);
432 elements = xnmalloc (cnt, sizeof *elements);
433 for (i = 0; i < cnt; i++)
440 h = heap_create (compare_elements, &aux_data);
442 while (insert_perm_cnt == 0 || next_permutation (insert, cnt))
444 unsigned int delete_perm_cnt = 0;
446 while (delete_perm_cnt == 0 || next_permutation (delete, cnt))
451 check (heap_is_empty (h));
453 for (i = 0; i < cnt; i++)
455 heap_insert (h, &elements[insert[i]].node);
458 check (heap_node_to_element (heap_minimum (h))->x == min);
459 check (heap_count (h) == i + 1);
462 for (i = 0; i < cnt; i++)
464 int new_min = min_int (delete + i + 1, cnt - i - 1);
465 heap_delete (h, &elements[delete[i]].node);
466 check (heap_count (h) == cnt - i - 1);
467 if (!heap_is_empty (h))
468 check (heap_node_to_element (heap_minimum (h))->x == new_min);
470 check (heap_is_empty (h));
473 check (delete_perm_cnt == factorial (cnt));
476 check (insert_perm_cnt == factorial (cnt));
484 /* Inserts a set of values into a heap, then changes them to a
485 different random set of values, then removes them in sorted
490 const int max_elems = 8;
493 for (cnt = 0; cnt <= max_elems; cnt++)
496 struct element *elements;
497 int *insert, *delete;
498 unsigned int insert_perm_cnt;
501 insert = xnmalloc (cnt, sizeof *insert);
502 delete = xnmalloc (cnt, sizeof *delete);
503 elements = xnmalloc (cnt, sizeof *elements);
504 for (i = 0; i < cnt; i++)
507 h = heap_create (compare_elements, &aux_data);
509 while (insert_perm_cnt == 0 || next_permutation (insert, cnt))
511 for (i = 0; i < cnt; i++)
512 elements[i].x = insert[i];
514 check (heap_is_empty (h));
515 for (i = 0; i < cnt; i++)
517 int new_min = min_int (insert, i + 1);
518 heap_insert (h, &elements[i].node);
519 check (heap_node_to_element (heap_minimum (h))->x == new_min);
520 check (heap_count (h) == i + 1);
523 for (i = 0; i < cnt; i++)
524 delete[i] = insert[i];
525 for (i = 0; i < cnt; i++)
527 elements[i].x = delete[i] = rand () % (cnt + 2) - 1;
528 heap_changed (h, &elements[i].node);
529 check (heap_node_to_element (heap_minimum (h))->x
530 == min_int (delete, cnt));
533 for (i = 0; i < cnt; i++)
535 int new_min = min_int (delete + i + 1, cnt - i - 1);
536 heap_delete (h, &elements[i].node);
537 check (heap_count (h) == cnt - i - 1);
538 if (!heap_is_empty (h))
539 check (heap_node_to_element (heap_minimum (h))->x == new_min);
541 check (heap_is_empty (h));
544 check (insert_perm_cnt == factorial (cnt));
552 /* Performs a random sequence of insertions and deletions in a
555 test_random_insert_delete (void)
557 const int max_elems = 64;
558 const int num_actions = 250000;
561 struct element *elements;
566 values = xnmalloc (max_elems, sizeof *values);
567 elements = xnmalloc (max_elems, sizeof *elements);
571 h = heap_create (compare_elements, &aux_data);
572 for (i = 0; i < num_actions; i++)
574 enum { INSERT, DELETE } action;
579 if (insert_chance < 9)
582 else if (cnt == max_elems)
585 if (insert_chance > 0)
589 action = rand () % 10 < insert_chance ? INSERT : DELETE;
591 if (action == INSERT)
595 new_value = rand () % max_elems;
596 values[cnt] = new_value;
597 elements[cnt].x = new_value;
599 heap_insert (h, &elements[cnt].node);
603 else if (action == DELETE)
607 del_idx = rand () % cnt;
608 heap_delete (h, &elements[del_idx].node);
613 values[del_idx] = values[cnt];
614 elements[del_idx] = elements[cnt];
615 heap_moved (h, &elements[del_idx].node);
621 check (heap_count (h) == cnt);
622 check (heap_is_empty (h) == (cnt == 0));
624 check (heap_node_to_element (heap_minimum (h))->x
625 == min_int (values, cnt));
637 const char *description;
638 void (*function) (void);
641 static const struct test tests[] =
644 "insert-no-dups-delete-min",
645 "insert (no dups), delete minimum values",
646 test_insert_no_dups_delete_min
649 "insert-with-dups-delete-min",
650 "insert with dups, delete minimum values",
651 test_insert_with_dups_delete_min
654 "insert-no-dups-delete-random",
655 "insert (no dups), delete in random order",
656 test_insert_no_dups_delete_random
660 "increase and decrease values",
664 "random-insert-delete",
665 "random insertions and deletions",
666 test_random_insert_delete
670 enum { N_TESTS = sizeof tests / sizeof *tests };
673 main (int argc, char *argv[])
679 fprintf (stderr, "exactly one argument required; use --help for help\n");
682 else if (!strcmp (argv[1], "--help"))
684 printf ("%s: test heap library\n"
685 "usage: %s TEST-NAME\n"
686 "where TEST-NAME is one of the following:\n",
688 for (i = 0; i < N_TESTS; i++)
689 printf (" %s\n %s\n", tests[i].name, tests[i].description);
694 for (i = 0; i < N_TESTS; i++)
695 if (!strcmp (argv[1], tests[i].name))
697 tests[i].function ();
701 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);