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 bt_* routines defined in bt.c.
18 This test program aims to be as comprehensive as possible.
19 "gcov -b" should report 100% coverage of lines and branches in
20 bt.c. "valgrind --leak-check=yes --show-reachable=yes" should
21 give a clean report. */
27 #include <libpspp/bt.h>
36 #include <libpspp/compiler.h>
38 /* Exit with a failure code.
39 (Place a breakpoint on this function while debugging.) */
46 /* If OK is not true, prints a message about failure on the
47 current source file and the given LINE and terminates. */
49 check_func (bool ok, int line)
53 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
58 /* Verifies that EXPR evaluates to true.
59 If not, prints a message citing the calling line number and
61 #define check(EXPR) check_func ((EXPR), __LINE__)
63 /* Prints a message about memory exhaustion and exits with a
68 printf ("virtual memory exhausted\n");
72 /* Allocates and returns N bytes of memory. */
89 xmemdup (const void *p, size_t n)
91 void *q = xmalloc (n);
96 /* Allocates and returns N * M bytes of memory. */
98 xnmalloc (size_t n, size_t m)
100 if ((size_t) -1 / m <= n)
102 return xmalloc (n * m);
105 /* Node type and support routines. */
107 /* Test data element. */
110 struct bt_node node; /* Embedded binary tree element. */
111 int data; /* Primary value. */
116 /* Returns the `struct element' that NODE is embedded within. */
117 static struct element *
118 bt_node_to_element (const struct bt_node *node)
120 return BT_DATA (node, struct element, node);
123 /* Compares the `x' values in A and B and returns a strcmp-type
124 return value. Verifies that AUX points to aux_data. */
126 compare_elements (const struct bt_node *a_, const struct bt_node *b_,
129 const struct element *a = bt_node_to_element (a_);
130 const struct element *b = bt_node_to_element (b_);
132 check (aux == &aux_data);
133 return a->data < b->data ? -1 : a->data > b->data;
136 /* Compares A and B and returns a strcmp-type return value. */
138 compare_ints_noaux (const void *a_, const void *b_)
143 return *a < *b ? -1 : *a > *b;
146 /* Swaps *A and *B. */
148 swap (int *a, int *b)
155 /* Reverses the order of the CNT integers starting at VALUES. */
157 reverse (int *values, size_t cnt)
163 swap (&values[i++], &values[--j]);
166 /* Arranges the CNT elements in VALUES into the lexicographically
167 next greater permutation. Returns true if successful.
168 If VALUES is already the lexicographically greatest
169 permutation of its elements (i.e. ordered from greatest to
170 smallest), arranges them into the lexicographically least
171 permutation (i.e. ordered from smallest to largest) and
174 next_permutation (int *values, size_t cnt)
182 if (values[i] < values[i + 1])
185 for (j = cnt - 1; values[i] >= values[j]; j--)
187 swap (values + i, values + j);
188 reverse (values + (i + 1), cnt - (i + 1));
193 reverse (values, cnt);
201 factorial (unsigned int n)
203 unsigned int value = 1;
209 /* Randomly shuffles the CNT elements in ARRAY, each of which is
210 SIZE bytes in size. */
212 random_shuffle (void *array_, size_t cnt, size_t size)
214 char *array = array_;
215 char *tmp = xmalloc (size);
218 for (i = 0; i < cnt; i++)
220 size_t j = rand () % (cnt - i) + i;
223 memcpy (tmp, array + j * size, size);
224 memcpy (array + j * size, array + i * size, size);
225 memcpy (array + i * size, tmp, size);
232 /* Calculates floor(log(n)/log(sqrt(2))). */
234 calculate_h_alpha (size_t n)
236 size_t thresholds[] =
238 0, 2, 2, 3, 4, 6, 8, 12, 16, 23, 32, 46, 64, 91, 128, 182, 256, 363,
239 512, 725, 1024, 1449, 2048, 2897, 4096, 5793, 8192, 11586, 16384,
240 23171, 32768, 46341, 65536, 92682, 131072, 185364, 262144, 370728,
241 524288, 741456, 1048576, 1482911, 2097152, 2965821, 4194304, 5931642,
242 8388608, 11863284, 16777216, 23726567, 33554432, 47453133, 67108864,
243 94906266, 134217728, 189812532, 268435456, 379625063, 536870912,
244 759250125, 1073741824, 1518500250, 2147483648, 3037000500,
246 size_t threshold_cnt = sizeof thresholds / sizeof *thresholds;
249 for (i = 0; i < threshold_cnt; i++)
250 if (thresholds[i] > n)
255 /* Returns the height of the tree rooted at NODE. */
257 get_height (struct bt_node *node)
263 int left = get_height (node->down[0]);
264 int right = get_height (node->down[1]);
265 return 1 + (left > right ? left : right);
269 /* Checks that BT is loosely alpha-height balanced, that is, that
270 its height is no more than h_alpha(count) + 1, where
271 h_alpha(n) = floor(log(n)/log(1/alpha)). */
273 check_balance (struct bt *bt)
275 /* In the notation of the Galperin and Rivest paper (and of
276 CLR), the height of a tree is the number of edges in the
277 longest path from the root to a leaf, so we have to subtract
278 1 from our measured height. */
279 int height = get_height (bt->root) - 1;
280 int max_height = calculate_h_alpha (bt_count (bt)) + 1;
281 check (height <= max_height);
284 /* Checks that BT contains the CNT ints in DATA, that its
285 structure is correct, and that certain operations on BT
286 produce the expected results. */
288 check_bt (struct bt *bt, const int data[], size_t cnt)
294 order = xmemdup (data, cnt * sizeof *data);
295 qsort (order, cnt, sizeof *order, compare_ints_noaux);
297 for (i = 0; i < cnt; i++)
303 p = bt_find (bt, &e.node);
305 p = bt_insert (bt, &e.node);
307 check (p != &e.node);
308 check (bt_node_to_element (p)->data == data[i]);
312 check (bt_find (bt, &e.node) == NULL);
318 check (bt_first (bt) == NULL);
319 check (bt_last (bt) == NULL);
320 check (bt_next (bt, NULL) == NULL);
321 check (bt_prev (bt, NULL) == NULL);
327 for (p = bt_first (bt), i = 0; i < cnt; p = bt_next (bt, p), i++)
328 check (bt_node_to_element (p)->data == order[i]);
331 for (p = bt_last (bt), i = 0; i < cnt; p = bt_prev (bt, p), i++)
332 check (bt_node_to_element (p)->data == order[cnt - i - 1]);
339 /* Inserts the CNT values from 0 to CNT - 1 (inclusive) into an
340 BT in the order specified by INSERTIONS, then deletes them in
341 the order specified by DELETIONS, checking the BT's contents
342 for correctness after each operation. */
344 test_insert_delete (const int insertions[],
345 const int deletions[],
348 struct element *elements;
352 elements = xnmalloc (cnt, sizeof *elements);
353 for (i = 0; i < cnt; i++)
354 elements[i].data = i;
356 bt_init (&bt, compare_elements, &aux_data);
357 check_bt (&bt, NULL, 0);
358 for (i = 0; i < cnt; i++)
360 check (bt_insert (&bt, &elements[insertions[i]].node) == NULL);
361 check_bt (&bt, insertions, i + 1);
363 for (i = 0; i < cnt; i++)
365 bt_delete (&bt, &elements[deletions[i]].node);
366 check_bt (&bt, deletions + i + 1, cnt - i - 1);
372 /* Inserts values into an BT in each possible order, then
373 removes them in each possible order, up to a specified maximum
376 test_insert_any_remove_any (void)
378 const int max_elems = 5;
381 for (cnt = 0; cnt <= max_elems; cnt++)
383 int *insertions, *deletions;
384 unsigned int ins_perm_cnt;
387 insertions = xnmalloc (cnt, sizeof *insertions);
388 deletions = xnmalloc (cnt, sizeof *deletions);
389 for (i = 0; i < cnt; i++)
392 for (ins_perm_cnt = 0;
393 ins_perm_cnt == 0 || next_permutation (insertions, cnt);
396 unsigned int del_perm_cnt;
399 for (i = 0; i < cnt; i++)
402 for (del_perm_cnt = 0;
403 del_perm_cnt == 0 || next_permutation (deletions, cnt);
405 test_insert_delete (insertions, deletions, cnt);
407 check (del_perm_cnt == factorial (cnt));
409 check (ins_perm_cnt == factorial (cnt));
416 /* Inserts values into an BT in each possible order, then
417 removes them in the same order, up to a specified maximum
420 test_insert_any_remove_same (void)
422 const int max_elems = 7;
425 for (cnt = 0; cnt <= max_elems; cnt++)
428 unsigned int permutation_cnt;
431 values = xnmalloc (cnt, sizeof *values);
432 for (i = 0; i < cnt; i++)
435 for (permutation_cnt = 0;
436 permutation_cnt == 0 || next_permutation (values, cnt);
438 test_insert_delete (values, values, cnt);
439 check (permutation_cnt == factorial (cnt));
445 /* Inserts values into an BT in each possible order, then
446 removes them in reverse order, up to a specified maximum
449 test_insert_any_remove_reverse (void)
451 const int max_elems = 7;
454 for (cnt = 0; cnt <= max_elems; cnt++)
456 int *insertions, *deletions;
457 unsigned int permutation_cnt;
460 insertions = xnmalloc (cnt, sizeof *insertions);
461 deletions = xnmalloc (cnt, sizeof *deletions);
462 for (i = 0; i < cnt; i++)
465 for (permutation_cnt = 0;
466 permutation_cnt == 0 || next_permutation (insertions, cnt);
469 memcpy (deletions, insertions, sizeof *insertions * cnt);
470 reverse (deletions, cnt);
472 test_insert_delete (insertions, deletions, cnt);
474 check (permutation_cnt == factorial (cnt));
481 /* Inserts and removes values in an BT in random orders. */
483 test_random_sequence (void)
485 const int max_elems = 128;
486 const int max_trials = 8;
489 for (cnt = 0; cnt <= max_elems; cnt += 2)
491 int *insertions, *deletions;
495 insertions = xnmalloc (cnt, sizeof *insertions);
496 deletions = xnmalloc (cnt, sizeof *deletions);
497 for (i = 0; i < cnt; i++)
499 for (i = 0; i < cnt; i++)
502 for (trial = 0; trial < max_trials; trial++)
504 random_shuffle (insertions, cnt, sizeof *insertions);
505 random_shuffle (deletions, cnt, sizeof *deletions);
507 test_insert_delete (insertions, deletions, cnt);
515 /* Inserts elements into an BT in ascending order. */
517 test_insert_ordered (void)
519 const int max_elems = 1024;
520 struct element *elements;
525 bt_init (&bt, compare_elements, &aux_data);
526 elements = xnmalloc (max_elems, sizeof *elements);
527 values = xnmalloc (max_elems, sizeof *values);
528 for (i = 0; i < max_elems; i++)
530 values[i] = elements[i].data = i;
531 check (bt_insert (&bt, &elements[i].node) == NULL);
532 check_bt (&bt, values, i + 1);
538 /* Tests bt_find_ge and bt_find_le. */
540 test_find_ge_le (void)
542 const int max_elems = 10;
543 struct element *elements;
545 unsigned int inc_pat;
547 elements = xnmalloc (max_elems, sizeof *elements);
548 values = xnmalloc (max_elems, sizeof *values);
549 for (inc_pat = 0; inc_pat < (1u << max_elems); inc_pat++)
555 /* Insert the values in the pattern into BT. */
556 bt_init (&bt, compare_elements, &aux_data);
557 for (i = 0; i < max_elems; i++)
558 if (inc_pat & (1u << i))
560 values[elem_cnt] = elements[elem_cnt].data = i;
561 check (bt_insert (&bt, &elements[elem_cnt].node) == NULL);
564 check_bt (&bt, values, elem_cnt);
566 /* Try find_ge and find_le for each possible element value. */
567 for (i = -1; i <= max_elems; i++)
570 struct bt_node *ge, *le;
574 for (j = 0; j < elem_cnt; j++)
576 if (ge == NULL && values[j] >= i)
577 ge = &elements[j].node;
579 le = &elements[j].node;
583 check (bt_find_ge (&bt, &tmp.node) == ge);
584 check (bt_find_le (&bt, &tmp.node) == le);
591 /* Inserts elements into an BT, then moves the nodes around in
596 const int max_elems = 128;
597 struct element *e[2];
603 bt_init (&bt, compare_elements, &aux_data);
604 e[0] = xnmalloc (max_elems, sizeof *e[0]);
605 e[1] = xnmalloc (max_elems, sizeof *e[1]);
606 values = xnmalloc (max_elems, sizeof *values);
608 for (i = 0; i < max_elems; i++)
610 values[i] = e[cur][i].data = i;
611 check (bt_insert (&bt, &e[cur][i].node) == NULL);
612 check_bt (&bt, values, i + 1);
614 for (j = 0; j <= i; j++)
616 e[!cur][j] = e[cur][j];
617 bt_moved (&bt, &e[!cur][j].node);
618 check_bt (&bt, values, i + 1);
627 /* Inserts values into an BT, then changes their values. */
631 const int max_elems = 6;
634 for (cnt = 0; cnt <= max_elems; cnt++)
636 int *values, *changed_values;
637 struct element *elements;
638 unsigned int permutation_cnt;
641 values = xnmalloc (cnt, sizeof *values);
642 changed_values = xnmalloc (cnt, sizeof *changed_values);
643 elements = xnmalloc (cnt, sizeof *elements);
644 for (i = 0; i < cnt; i++)
647 for (permutation_cnt = 0;
648 permutation_cnt == 0 || next_permutation (values, cnt);
651 for (i = 0; i < cnt; i++)
654 for (j = 0; j <= cnt; j++)
657 struct bt_node *changed_retval;
659 bt_init (&bt, compare_elements, &aux_data);
661 /* Add to BT in order. */
662 for (k = 0; k < cnt; k++)
665 elements[n].data = n;
666 check (bt_insert (&bt, &elements[n].node) == NULL);
668 check_bt (&bt, values, cnt);
670 /* Change value i to j. */
671 elements[i].data = j;
672 for (k = 0; k < cnt; k++)
673 changed_values[k] = k;
674 changed_retval = bt_changed (&bt, &elements[i].node);
675 if (i != j && j < cnt)
677 /* Will cause duplicate. */
678 check (changed_retval == &elements[j].node);
679 changed_values[i] = changed_values[cnt - 1];
680 check_bt (&bt, changed_values, cnt - 1);
685 check (changed_retval == NULL);
686 changed_values[i] = j;
687 check_bt (&bt, changed_values, cnt);
692 check (permutation_cnt == factorial (cnt));
695 free (changed_values);
705 const char *description;
706 void (*function) (void);
709 static const struct test tests[] =
712 "insert-any-remove-any",
713 "insert any order, delete any order",
714 test_insert_any_remove_any
717 "insert-any-remove-same",
718 "insert any order, delete same order",
719 test_insert_any_remove_same
722 "insert-any-remove-reverse",
723 "insert any order, delete reverse order",
724 test_insert_any_remove_reverse
728 "insert and delete in random sequence",
733 "insert in ascending order",
738 "find_ge and find_le",
743 "move elements around in memory",
748 "change key data in nodes",
753 enum { N_TESTS = sizeof tests / sizeof *tests };
756 main (int argc, char *argv[])
762 fprintf (stderr, "exactly one argument required; use --help for help\n");
765 else if (!strcmp (argv[1], "--help"))
767 printf ("%s: test balanced tree\n"
768 "usage: %s TEST-NAME\n"
769 "where TEST-NAME is one of the following:\n",
771 for (i = 0; i < N_TESTS; i++)
772 printf (" %s\n %s\n", tests[i].name, tests[i].description);
777 for (i = 0; i < N_TESTS; i++)
778 if (!strcmp (argv[1], tests[i].name))
780 tests[i].function ();
784 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);