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>
37 #include <libpspp/compiler.h>
39 /* Exit with a failure code.
40 (Place a breakpoint on this function while debugging.) */
47 /* If OK is not true, prints a message about failure on the
48 current source file and the given LINE and terminates. */
50 check_func (bool ok, int line)
54 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
59 /* Verifies that EXPR evaluates to true.
60 If not, prints a message citing the calling line number and
62 #define check(EXPR) check_func ((EXPR), __LINE__)
64 /* Prints a message about memory exhaustion and exits with a
69 printf ("virtual memory exhausted\n");
73 /* Allocates and returns N bytes of memory. */
90 xmemdup (const void *p, size_t n)
92 void *q = xmalloc (n);
97 /* Allocates and returns N * M bytes of memory. */
99 xnmalloc (size_t n, size_t m)
101 if ((size_t) -1 / m <= n)
103 return xmalloc (n * m);
106 /* Node type and support routines. */
108 /* Test data element. */
111 struct bt_node node; /* Embedded binary tree element. */
112 int data; /* Primary value. */
117 /* Returns the `struct element' that NODE is embedded within. */
118 static struct element *
119 bt_node_to_element (const struct bt_node *node)
121 return bt_data (node, struct element, node);
124 /* Compares the `x' values in A and B and returns a strcmp-type
125 return value. Verifies that AUX points to aux_data. */
127 compare_elements (const struct bt_node *a_, const struct bt_node *b_,
130 const struct element *a = bt_node_to_element (a_);
131 const struct element *b = bt_node_to_element (b_);
133 check (aux == &aux_data);
134 return a->data < b->data ? -1 : a->data > b->data;
137 /* Compares A and B and returns a strcmp-type return value. */
139 compare_ints_noaux (const void *a_, const void *b_)
144 return *a < *b ? -1 : *a > *b;
147 /* Swaps *A and *B. */
149 swap (int *a, int *b)
156 /* Reverses the order of the CNT integers starting at VALUES. */
158 reverse (int *values, size_t cnt)
164 swap (&values[i++], &values[--j]);
167 /* Arranges the CNT elements in VALUES into the lexicographically
168 next greater permutation. Returns true if successful.
169 If VALUES is already the lexicographically greatest
170 permutation of its elements (i.e. ordered from greatest to
171 smallest), arranges them into the lexicographically least
172 permutation (i.e. ordered from smallest to largest) and
175 next_permutation (int *values, size_t cnt)
183 if (values[i] < values[i + 1])
186 for (j = cnt - 1; values[i] >= values[j]; j--)
188 swap (values + i, values + j);
189 reverse (values + (i + 1), cnt - (i + 1));
194 reverse (values, cnt);
202 factorial (unsigned int n)
204 unsigned int value = 1;
210 /* Randomly shuffles the CNT elements in ARRAY, each of which is
211 SIZE bytes in size. */
213 random_shuffle (void *array_, size_t cnt, size_t size)
215 char *array = array_;
216 char *tmp = xmalloc (size);
219 for (i = 0; i < cnt; i++)
221 size_t j = rand () % (cnt - i) + i;
224 memcpy (tmp, array + j * size, size);
225 memcpy (array + j * size, array + i * size, size);
226 memcpy (array + i * size, tmp, size);
233 /* Calculates floor(log(n)/log(sqrt(2))). */
235 calculate_h_alpha (size_t n)
237 size_t thresholds[] =
239 0, 2, 2, 3, 4, 6, 8, 12, 16, 23, 32, 46, 64, 91, 128, 182, 256, 363,
240 512, 725, 1024, 1449, 2048, 2897, 4096, 5793, 8192, 11586, 16384,
241 23171, 32768, 46341, 65536, 92682, 131072, 185364, 262144, 370728,
242 524288, 741456, 1048576, 1482911, 2097152, 2965821, 4194304, 5931642,
243 8388608, 11863284, 16777216, 23726567, 33554432, 47453133, 67108864,
244 94906266, 134217728, 189812532, 268435456, 379625063, 536870912,
245 759250125, 1073741824, 1518500250, 2147483648, 3037000500,
247 size_t threshold_cnt = sizeof thresholds / sizeof *thresholds;
250 for (i = 0; i < threshold_cnt; i++)
251 if (thresholds[i] > n)
256 /* Returns the height of the tree rooted at NODE. */
258 get_height (struct bt_node *node)
264 int left = get_height (node->down[0]);
265 int right = get_height (node->down[1]);
266 return 1 + (left > right ? left : right);
270 /* Checks that BT is loosely alpha-height balanced, that is, that
271 its height is no more than h_alpha(count) + 1, where
272 h_alpha(n) = floor(log(n)/log(1/alpha)). */
274 check_balance (struct bt *bt)
276 /* In the notation of the Galperin and Rivest paper (and of
277 CLR), the height of a tree is the number of edges in the
278 longest path from the root to a leaf, so we have to subtract
279 1 from our measured height. */
280 int height = get_height (bt->root) - 1;
281 int max_height = calculate_h_alpha (bt_count (bt)) + 1;
282 check (height <= max_height);
285 /* Checks that BT contains the CNT ints in DATA, that its
286 structure is correct, and that certain operations on BT
287 produce the expected results. */
289 check_bt (struct bt *bt, const int data[], size_t cnt)
295 order = xmemdup (data, cnt * sizeof *data);
296 qsort (order, cnt, sizeof *order, compare_ints_noaux);
298 for (i = 0; i < cnt; i++)
304 p = bt_find (bt, &e.node);
306 p = bt_insert (bt, &e.node);
308 check (p != &e.node);
309 check (bt_node_to_element (p)->data == data[i]);
313 check (bt_find (bt, &e.node) == NULL);
319 check (bt_first (bt) == NULL);
320 check (bt_last (bt) == NULL);
321 check (bt_next (bt, NULL) == NULL);
322 check (bt_prev (bt, NULL) == NULL);
328 for (p = bt_first (bt), i = 0; i < cnt; p = bt_next (bt, p), i++)
329 check (bt_node_to_element (p)->data == order[i]);
332 for (p = bt_last (bt), i = 0; i < cnt; p = bt_prev (bt, p), i++)
333 check (bt_node_to_element (p)->data == order[cnt - i - 1]);
340 /* Inserts the CNT values from 0 to CNT - 1 (inclusive) into an
341 BT in the order specified by INSERTIONS, then deletes them in
342 the order specified by DELETIONS, checking the BT's contents
343 for correctness after each operation. */
345 test_insert_delete (const int insertions[],
346 const int deletions[],
349 struct element *elements;
353 elements = xnmalloc (cnt, sizeof *elements);
354 for (i = 0; i < cnt; i++)
355 elements[i].data = i;
357 bt_init (&bt, compare_elements, &aux_data);
358 check_bt (&bt, NULL, 0);
359 for (i = 0; i < cnt; i++)
361 check (bt_insert (&bt, &elements[insertions[i]].node) == NULL);
362 check_bt (&bt, insertions, i + 1);
364 for (i = 0; i < cnt; i++)
366 bt_delete (&bt, &elements[deletions[i]].node);
367 check_bt (&bt, deletions + i + 1, cnt - i - 1);
373 /* Inserts values into an BT in each possible order, then
374 removes them in each possible order, up to a specified maximum
377 test_insert_any_remove_any (void)
379 const int max_elems = 5;
382 for (cnt = 0; cnt <= max_elems; cnt++)
384 int *insertions, *deletions;
385 unsigned int ins_perm_cnt;
388 insertions = xnmalloc (cnt, sizeof *insertions);
389 deletions = xnmalloc (cnt, sizeof *deletions);
390 for (i = 0; i < cnt; i++)
393 for (ins_perm_cnt = 0;
394 ins_perm_cnt == 0 || next_permutation (insertions, cnt);
397 unsigned int del_perm_cnt;
400 for (i = 0; i < cnt; i++)
403 for (del_perm_cnt = 0;
404 del_perm_cnt == 0 || next_permutation (deletions, cnt);
406 test_insert_delete (insertions, deletions, cnt);
408 check (del_perm_cnt == factorial (cnt));
410 check (ins_perm_cnt == factorial (cnt));
417 /* Inserts values into an BT in each possible order, then
418 removes them in the same order, up to a specified maximum
421 test_insert_any_remove_same (void)
423 const int max_elems = 7;
426 for (cnt = 0; cnt <= max_elems; cnt++)
429 unsigned int permutation_cnt;
432 values = xnmalloc (cnt, sizeof *values);
433 for (i = 0; i < cnt; i++)
436 for (permutation_cnt = 0;
437 permutation_cnt == 0 || next_permutation (values, cnt);
439 test_insert_delete (values, values, cnt);
440 check (permutation_cnt == factorial (cnt));
446 /* Inserts values into an BT in each possible order, then
447 removes them in reverse order, up to a specified maximum
450 test_insert_any_remove_reverse (void)
452 const int max_elems = 7;
455 for (cnt = 0; cnt <= max_elems; cnt++)
457 int *insertions, *deletions;
458 unsigned int permutation_cnt;
461 insertions = xnmalloc (cnt, sizeof *insertions);
462 deletions = xnmalloc (cnt, sizeof *deletions);
463 for (i = 0; i < cnt; i++)
466 for (permutation_cnt = 0;
467 permutation_cnt == 0 || next_permutation (insertions, cnt);
470 memcpy (deletions, insertions, sizeof *insertions * cnt);
471 reverse (deletions, cnt);
473 test_insert_delete (insertions, deletions, cnt);
475 check (permutation_cnt == factorial (cnt));
482 /* Inserts and removes values in an BT in random orders. */
484 test_random_sequence (void)
486 const int max_elems = 128;
487 const int max_trials = 8;
490 for (cnt = 0; cnt <= max_elems; cnt += 2)
492 int *insertions, *deletions;
496 insertions = xnmalloc (cnt, sizeof *insertions);
497 deletions = xnmalloc (cnt, sizeof *deletions);
498 for (i = 0; i < cnt; i++)
500 for (i = 0; i < cnt; i++)
503 for (trial = 0; trial < max_trials; trial++)
505 random_shuffle (insertions, cnt, sizeof *insertions);
506 random_shuffle (deletions, cnt, sizeof *deletions);
508 test_insert_delete (insertions, deletions, cnt);
516 /* Inserts elements into an BT in ascending order. */
518 test_insert_ordered (void)
520 const int max_elems = 1024;
521 struct element *elements;
526 bt_init (&bt, compare_elements, &aux_data);
527 elements = xnmalloc (max_elems, sizeof *elements);
528 values = xnmalloc (max_elems, sizeof *values);
529 for (i = 0; i < max_elems; i++)
531 values[i] = elements[i].data = i;
532 check (bt_insert (&bt, &elements[i].node) == NULL);
533 check_bt (&bt, values, i + 1);
539 /* Tests bt_find_ge and bt_find_le. */
541 test_find_ge_le (void)
543 const int max_elems = 10;
544 struct element *elements;
546 unsigned int inc_pat;
548 elements = xnmalloc (max_elems, sizeof *elements);
549 values = xnmalloc (max_elems, sizeof *values);
550 for (inc_pat = 0; inc_pat < (1u << max_elems); inc_pat++)
556 /* Insert the values in the pattern into BT. */
557 bt_init (&bt, compare_elements, &aux_data);
558 for (i = 0; i < max_elems; i++)
559 if (inc_pat & (1u << i))
561 values[elem_cnt] = elements[elem_cnt].data = i;
562 check (bt_insert (&bt, &elements[elem_cnt].node) == NULL);
565 check_bt (&bt, values, elem_cnt);
567 /* Try find_ge and find_le for each possible element value. */
568 for (i = -1; i <= max_elems; i++)
571 struct bt_node *ge, *le;
575 for (j = 0; j < elem_cnt; j++)
577 if (ge == NULL && values[j] >= i)
578 ge = &elements[j].node;
580 le = &elements[j].node;
584 check (bt_find_ge (&bt, &tmp.node) == ge);
585 check (bt_find_le (&bt, &tmp.node) == le);
592 /* Inserts elements into an BT, then moves the nodes around in
597 const int max_elems = 128;
598 struct element *e[2];
604 bt_init (&bt, compare_elements, &aux_data);
605 e[0] = xnmalloc (max_elems, sizeof *e[0]);
606 e[1] = xnmalloc (max_elems, sizeof *e[1]);
607 values = xnmalloc (max_elems, sizeof *values);
609 for (i = 0; i < max_elems; i++)
611 values[i] = e[cur][i].data = i;
612 check (bt_insert (&bt, &e[cur][i].node) == NULL);
613 check_bt (&bt, values, i + 1);
615 for (j = 0; j <= i; j++)
617 e[!cur][j] = e[cur][j];
618 bt_moved (&bt, &e[!cur][j].node);
619 check_bt (&bt, values, i + 1);
628 /* Inserts values into an BT, then changes their values. */
632 const int max_elems = 6;
635 for (cnt = 0; cnt <= max_elems; cnt++)
637 int *values, *changed_values;
638 struct element *elements;
639 unsigned int permutation_cnt;
642 values = xnmalloc (cnt, sizeof *values);
643 changed_values = xnmalloc (cnt, sizeof *changed_values);
644 elements = xnmalloc (cnt, sizeof *elements);
645 for (i = 0; i < cnt; i++)
648 for (permutation_cnt = 0;
649 permutation_cnt == 0 || next_permutation (values, cnt);
652 for (i = 0; i < cnt; i++)
655 for (j = 0; j <= cnt; j++)
658 struct bt_node *changed_retval;
660 bt_init (&bt, compare_elements, &aux_data);
662 /* Add to BT in order. */
663 for (k = 0; k < cnt; k++)
666 elements[n].data = n;
667 check (bt_insert (&bt, &elements[n].node) == NULL);
669 check_bt (&bt, values, cnt);
671 /* Change value i to j. */
672 elements[i].data = j;
673 for (k = 0; k < cnt; k++)
674 changed_values[k] = k;
675 changed_retval = bt_changed (&bt, &elements[i].node);
676 if (i != j && j < cnt)
678 /* Will cause duplicate. */
679 check (changed_retval == &elements[j].node);
680 changed_values[i] = changed_values[cnt - 1];
681 check_bt (&bt, changed_values, cnt - 1);
686 check (changed_retval == NULL);
687 changed_values[i] = j;
688 check_bt (&bt, changed_values, cnt);
693 check (permutation_cnt == factorial (cnt));
696 free (changed_values);
706 const char *description;
707 void (*function) (void);
710 static const struct test tests[] =
713 "insert-any-remove-any",
714 "insert any order, delete any order",
715 test_insert_any_remove_any
718 "insert-any-remove-same",
719 "insert any order, delete same order",
720 test_insert_any_remove_same
723 "insert-any-remove-reverse",
724 "insert any order, delete reverse order",
725 test_insert_any_remove_reverse
729 "insert and delete in random sequence",
734 "insert in ascending order",
739 "find_ge and find_le",
744 "move elements around in memory",
749 "change key data in nodes",
754 enum { N_TESTS = sizeof tests / sizeof *tests };
757 main (int argc, char *argv[])
763 fprintf (stderr, "exactly one argument required; use --help for help\n");
766 else if (!strcmp (argv[1], "--help"))
768 printf ("%s: test balanced tree\n"
769 "usage: %s TEST-NAME\n"
770 "where TEST-NAME is one of the following:\n",
772 for (i = 0; i < N_TESTS; i++)
773 printf (" %s\n %s\n", tests[i].name, tests[i].description);
778 for (i = 0; i < N_TESTS; i++)
779 if (!strcmp (argv[1], tests[i].name))
781 tests[i].function ();
785 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);