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 N integers starting at VALUES. */
123 reverse (int *values
, size_t n
)
129 swap (&values
[i
++], &values
[--j
]);
132 /* Arranges the N 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 n
)
148 if (values
[i
] < values
[i
+ 1])
151 for (j
= n
- 1; values
[i
] >= values
[j
]; j
--)
153 swap (values
+ i
, values
+ j
);
154 reverse (values
+ (i
+ 1), n
- (i
+ 1));
167 factorial (unsigned int n
)
169 unsigned int value
= 1;
175 /* Returns the number of permutations of the N values in
176 VALUES. If VALUES contains duplicates, they must be
179 expected_perms (int *values
, size_t n
)
182 unsigned int n_perms
;
184 n_perms
= factorial (n
);
185 for (i
= 0; i
< n
; i
= j
)
187 for (j
= i
+ 1; j
< n
; j
++)
188 if (values
[i
] != values
[j
])
190 n_perms
/= 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 (n
= 0; n
<= max_elems
; n
++)
282 struct element
*elements
;
284 unsigned int n_permutations
;
287 values
= xnmalloc (n
, sizeof *values
);
288 elements
= xnmalloc (n
, sizeof *elements
);
289 for (i
= 0; i
< n
; i
++)
292 h
= heap_create (compare_elements
, &aux_data
);
294 while (n_permutations
== 0 || next_permutation (values
, n
))
298 for (i
= 0; i
< n
; i
++)
299 elements
[i
].x
= values
[i
];
301 check (heap_is_empty (h
));
302 for (i
= 0; i
< n
; 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
< n
; 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 (n_permutations
== factorial (n
));
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;
336 for (int n_elems
= 1; n_elems
<= max_elems
; n_elems
++)
338 unsigned int n_compositions
;
343 struct element
*elements
;
346 dups
= xnmalloc (n_elems
, sizeof *dups
);
347 values
= xnmalloc (n_elems
, sizeof *values
);
348 sorted_values
= xnmalloc (n_elems
, sizeof *sorted_values
);
349 elements
= xnmalloc (n_elems
, sizeof *elements
);
353 while (next_composition (n_elems
, &n_uniques
, dups
))
357 unsigned int n_permutations
;
360 for (i
= 0; i
< n_uniques
; i
++)
361 for (j
= 0; j
< dups
[i
]; j
++)
364 sorted_values
[k
] = i
;
367 check (k
== n_elems
);
369 h
= heap_create (compare_elements
, &aux_data
);
371 while (n_permutations
== 0 || next_permutation (values
, n_elems
))
375 for (i
= 0; i
< n_elems
; i
++)
376 elements
[i
].x
= values
[i
];
379 check (heap_is_empty (h
));
380 for (i
= 0; i
< n_elems
; i
++)
382 heap_insert (h
, &elements
[i
].node
);
385 check (heap_node_to_element (heap_minimum (h
))->x
== min
);
386 check (heap_count (h
) == i
+ 1);
389 for (i
= 0; i
< n_elems
; i
++)
391 struct element
*min
= heap_node_to_element (heap_minimum (h
));
392 check (min
->x
== sorted_values
[i
]);
393 heap_delete (h
, heap_minimum (h
));
395 check (heap_is_empty (h
));
398 check (n_permutations
== expected_perms (values
, n_elems
));
403 check (n_compositions
== 1 << (n_elems
- 1));
407 free (sorted_values
);
412 /* Inserts a sequence without duplicates into a heap, then
413 deletes them in a different order. */
415 test_insert_no_dups_delete_random (void)
417 const int max_elems
= 5;
420 for (n
= 0; n
<= max_elems
; n
++)
423 struct element
*elements
;
424 int *insert
, *delete;
425 unsigned int insert_n_perms
;
428 insert
= xnmalloc (n
, sizeof *insert
);
429 delete = xnmalloc (n
, sizeof *delete);
430 elements
= xnmalloc (n
, sizeof *elements
);
431 for (i
= 0; i
< n
; i
++)
438 h
= heap_create (compare_elements
, &aux_data
);
440 while (insert_n_perms
== 0 || next_permutation (insert
, n
))
442 unsigned int delete_n_perms
= 0;
444 while (delete_n_perms
== 0 || next_permutation (delete, n
))
449 check (heap_is_empty (h
));
451 for (i
= 0; i
< n
; i
++)
453 heap_insert (h
, &elements
[insert
[i
]].node
);
456 check (heap_node_to_element (heap_minimum (h
))->x
== min
);
457 check (heap_count (h
) == i
+ 1);
460 for (i
= 0; i
< n
; i
++)
462 int new_min
= min_int (delete + i
+ 1, n
- i
- 1);
463 heap_delete (h
, &elements
[delete[i
]].node
);
464 check (heap_count (h
) == n
- i
- 1);
465 if (!heap_is_empty (h
))
466 check (heap_node_to_element (heap_minimum (h
))->x
== new_min
);
468 check (heap_is_empty (h
));
471 check (delete_n_perms
== factorial (n
));
474 check (insert_n_perms
== factorial (n
));
482 /* Inserts a set of values into a heap, then changes them to a
483 different random set of values, then removes them in sorted
488 const int max_elems
= 8;
491 for (n
= 0; n
<= max_elems
; n
++)
494 struct element
*elements
;
495 int *insert
, *delete;
496 unsigned int insert_n_perms
;
499 insert
= xnmalloc (n
, sizeof *insert
);
500 delete = xnmalloc (n
, sizeof *delete);
501 elements
= xnmalloc (n
, sizeof *elements
);
502 for (i
= 0; i
< n
; i
++)
505 h
= heap_create (compare_elements
, &aux_data
);
507 while (insert_n_perms
== 0 || next_permutation (insert
, n
))
509 for (i
= 0; i
< n
; i
++)
510 elements
[i
].x
= insert
[i
];
512 check (heap_is_empty (h
));
513 for (i
= 0; i
< n
; i
++)
515 int new_min
= min_int (insert
, i
+ 1);
516 heap_insert (h
, &elements
[i
].node
);
517 check (heap_node_to_element (heap_minimum (h
))->x
== new_min
);
518 check (heap_count (h
) == i
+ 1);
521 for (i
= 0; i
< n
; i
++)
522 delete[i
] = insert
[i
];
523 for (i
= 0; i
< n
; i
++)
525 elements
[i
].x
= delete[i
] = rand () % (n
+ 2) - 1;
526 heap_changed (h
, &elements
[i
].node
);
527 check (heap_node_to_element (heap_minimum (h
))->x
528 == min_int (delete, n
));
531 for (i
= 0; i
< n
; i
++)
533 int new_min
= min_int (delete + i
+ 1, n
- i
- 1);
534 heap_delete (h
, &elements
[i
].node
);
535 check (heap_count (h
) == n
- i
- 1);
536 if (!heap_is_empty (h
))
537 check (heap_node_to_element (heap_minimum (h
))->x
== new_min
);
539 check (heap_is_empty (h
));
542 check (insert_n_perms
== factorial (n
));
550 /* Performs a random sequence of insertions and deletions in a
553 test_random_insert_delete (void)
555 const int max_elems
= 64;
556 const int num_actions
= 250000;
559 struct element
*elements
;
564 values
= xnmalloc (max_elems
, sizeof *values
);
565 elements
= xnmalloc (max_elems
, sizeof *elements
);
569 h
= heap_create (compare_elements
, &aux_data
);
570 for (i
= 0; i
< num_actions
; i
++)
572 enum { INSERT
, DELETE
} action
;
577 if (insert_chance
< 9)
580 else if (n
== max_elems
)
583 if (insert_chance
> 0)
587 action
= rand () % 10 < insert_chance
? INSERT
: DELETE
;
589 if (action
== INSERT
)
593 new_value
= rand () % max_elems
;
594 values
[n
] = new_value
;
595 elements
[n
].x
= new_value
;
597 heap_insert (h
, &elements
[n
].node
);
601 else if (action
== DELETE
)
605 del_idx
= rand () % n
;
606 heap_delete (h
, &elements
[del_idx
].node
);
611 values
[del_idx
] = values
[n
];
612 elements
[del_idx
] = elements
[n
];
613 heap_moved (h
, &elements
[del_idx
].node
);
619 check (heap_count (h
) == n
);
620 check (heap_is_empty (h
) == (n
== 0));
622 check (heap_node_to_element (heap_minimum (h
))->x
623 == min_int (values
, n
));
635 const char *description
;
636 void (*function
) (void);
639 static const struct test tests
[] =
642 "insert-no-dups-delete-min",
643 "insert (no dups), delete minimum values",
644 test_insert_no_dups_delete_min
647 "insert-with-dups-delete-min",
648 "insert with dups, delete minimum values",
649 test_insert_with_dups_delete_min
652 "insert-no-dups-delete-random",
653 "insert (no dups), delete in random order",
654 test_insert_no_dups_delete_random
658 "increase and decrease values",
662 "random-insert-delete",
663 "random insertions and deletions",
664 test_random_insert_delete
668 enum { N_TESTS
= sizeof tests
/ sizeof *tests
};
671 main (int argc
, char *argv
[])
677 fprintf (stderr
, "exactly one argument required; use --help for help\n");
680 else if (!strcmp (argv
[1], "--help"))
682 printf ("%s: test heap library\n"
683 "usage: %s TEST-NAME\n"
684 "where TEST-NAME is one of the following:\n",
686 for (i
= 0; i
< N_TESTS
; i
++)
687 printf (" %s\n %s\n", tests
[i
].name
, tests
[i
].description
);
692 for (i
= 0; i
< N_TESTS
; i
++)
693 if (!strcmp (argv
[1], tests
[i
].name
))
695 tests
[i
].function ();
699 fprintf (stderr
, "unknown test %s; use --help for help\n", argv
[1]);