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 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 int old_value, old_min, new_min;
528 old_min = min_int (delete, cnt);
529 old_value = delete[i];
530 elements[i].x = delete[i] = rand () % (cnt + 2) - 1;
531 new_min = min_int (delete, cnt);
532 heap_changed (h, &elements[i].node);
533 check (heap_node_to_element (heap_minimum (h))->x
534 == min_int (delete, cnt));
537 for (i = 0; i < cnt; i++)
539 int new_min = min_int (delete + i + 1, cnt - i - 1);
540 heap_delete (h, &elements[i].node);
541 check (heap_count (h) == cnt - i - 1);
542 if (!heap_is_empty (h))
543 check (heap_node_to_element (heap_minimum (h))->x == new_min);
545 check (heap_is_empty (h));
548 check (insert_perm_cnt == factorial (cnt));
556 /* Performs a random sequence of insertions and deletions in a
559 test_random_insert_delete (void)
561 const int max_elems = 64;
562 const int num_actions = 250000;
565 struct element *elements;
570 values = xnmalloc (max_elems, sizeof *values);
571 elements = xnmalloc (max_elems, sizeof *elements);
575 h = heap_create (compare_elements, &aux_data);
576 for (i = 0; i < num_actions; i++)
578 enum { INSERT, DELETE } action;
583 if (insert_chance < 9)
586 else if (cnt == max_elems)
589 if (insert_chance > 0)
593 action = rand () % 10 < insert_chance ? INSERT : DELETE;
595 if (action == INSERT)
600 new_value = rand () % max_elems;
601 values[cnt] = new_value;
602 elements[cnt].x = new_value;
604 heap_insert (h, &elements[cnt].node);
606 old_min = min_int (values, cnt);
610 else if (action == DELETE)
614 int old_min, new_min;
616 old_min = min_int (values, cnt);
618 del_idx = rand () % cnt;
619 del_value = values[del_idx];
620 heap_delete (h, &elements[del_idx].node);
625 values[del_idx] = values[cnt];
626 elements[del_idx] = elements[cnt];
627 heap_moved (h, &elements[del_idx].node);
630 new_min = min_int (values, cnt);
635 check (heap_count (h) == cnt);
636 check (heap_is_empty (h) == (cnt == 0));
638 check (heap_node_to_element (heap_minimum (h))->x
639 == min_int (values, cnt));
651 const char *description;
652 void (*function) (void);
655 static const struct test tests[] =
658 "insert-no-dups-delete-min",
659 "insert (no dups), delete minimum values",
660 test_insert_no_dups_delete_min
663 "insert-with-dups-delete-min",
664 "insert with dups, delete minimum values",
665 test_insert_with_dups_delete_min
668 "insert-no-dups-delete-random",
669 "insert (no dups), delete in random order",
670 test_insert_no_dups_delete_random
674 "increase and decrease values",
678 "random-insert-delete",
679 "random insertions and deletions",
680 test_random_insert_delete
684 enum { N_TESTS = sizeof tests / sizeof *tests };
687 main (int argc, char *argv[])
693 fprintf (stderr, "exactly one argument required; use --help for help\n");
696 else if (!strcmp (argv[1], "--help"))
698 printf ("%s: test heap library\n"
699 "usage: %s TEST-NAME\n"
700 "where TEST-NAME is one of the following:\n",
702 for (i = 0; i < N_TESTS; i++)
703 printf (" %s\n %s\n", tests[i].name, tests[i].description);
708 for (i = 0; i < N_TESTS; i++)
709 if (!strcmp (argv[1], tests[i].name))
711 tests[i].function ();
715 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);