Windows build: Adapted icon names to org.fsf.pspp naming
[pspp.git] / tests / libpspp / bt-test.c
blob60438df20abcbe5fdc9b617ed7671f0dcd0e44e9
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. */
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <libpspp/bt.h>
29 #include <stdbool.h>
30 #include <stddef.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
36 #include <libpspp/compiler.h>
38 /* Exit with a failure code.
39 (Place a breakpoint on this function while debugging.) */
40 static void
41 check_die (void)
43 exit (EXIT_FAILURE);
46 /* If OK is not true, prints a message about failure on the
47 current source file and the given LINE and terminates. */
48 static void
49 check_func (bool ok, int line)
51 if (!ok)
53 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
54 check_die ();
58 /* Verifies that EXPR evaluates to true.
59 If not, prints a message citing the calling line number and
60 terminates. */
61 #define check(EXPR) check_func ((EXPR), __LINE__)
63 /* Prints a message about memory exhaustion and exits with a
64 failure code. */
65 static void
66 xalloc_die (void)
68 printf ("virtual memory exhausted\n");
69 exit (EXIT_FAILURE);
72 /* Allocates and returns N bytes of memory. */
73 static void *
74 xmalloc (size_t n)
76 if (n != 0)
78 void *p = malloc (n);
79 if (p == NULL)
80 xalloc_die ();
82 return p;
84 else
85 return NULL;
88 static void *
89 xmemdup (const void *p, size_t n)
91 void *q = xmalloc (n);
92 memcpy (q, p, n);
93 return q;
96 /* Allocates and returns N * M bytes of memory. */
97 static void *
98 xnmalloc (size_t n, size_t m)
100 if ((size_t) -1 / m <= n)
101 xalloc_die ();
102 return xmalloc (n * m);
105 /* Node type and support routines. */
107 /* Test data element. */
108 struct element
110 struct bt_node node; /* Embedded binary tree element. */
111 int data; /* Primary value. */
114 static int aux_data;
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. */
125 static int
126 compare_elements (const struct bt_node *a_, const struct bt_node *b_,
127 const void *aux)
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. */
137 static int
138 compare_ints_noaux (const void *a_, const void *b_)
140 const int *a = a_;
141 const int *b = b_;
143 return *a < *b ? -1 : *a > *b;
146 /* Swaps *A and *B. */
147 static void
148 swap (int *a, int *b)
150 int t = *a;
151 *a = *b;
152 *b = t;
155 /* Reverses the order of the N integers starting at VALUES. */
156 static void
157 reverse (int *values, size_t n)
159 size_t i = 0;
160 size_t j = n;
162 while (j > i)
163 swap (&values[i++], &values[--j]);
166 /* Arranges the N 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
172 returns false. */
173 static bool
174 next_permutation (int *values, size_t n)
176 if (n > 0)
178 size_t i = n - 1;
179 while (i != 0)
181 i--;
182 if (values[i] < values[i + 1])
184 size_t j;
185 for (j = n - 1; values[i] >= values[j]; j--)
186 continue;
187 swap (values + i, values + j);
188 reverse (values + (i + 1), n - (i + 1));
189 return true;
193 reverse (values, n);
196 return false;
199 /* Returns N!. */
200 static unsigned int
201 factorial (unsigned int n)
203 unsigned int value = 1;
204 while (n > 1)
205 value *= n--;
206 return value;
209 /* Randomly shuffles the N elements in ARRAY, each of which is
210 SIZE bytes in size. */
211 static void
212 random_shuffle (void *array_, size_t n, size_t size)
214 char *array = array_;
215 char *tmp = xmalloc (size);
216 size_t i;
218 for (i = 0; i < n; i++)
220 size_t j = rand () % (n - i) + i;
221 if (i != j)
223 memcpy (tmp, array + j * size, size);
224 memcpy (array + j * size, array + i * size, size);
225 memcpy (array + i * size, tmp, size);
229 free (tmp);
232 /* Calculates floor(log(n)/log(sqrt(2))). */
233 static int
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 n_thresholds = sizeof thresholds / sizeof *thresholds;
247 size_t i;
249 for (i = 0; i < n_thresholds; i++)
250 if (thresholds[i] > n)
251 break;
252 return i - 1;
255 /* Returns the height of the tree rooted at NODE. */
256 static int
257 get_height (struct bt_node *node)
259 if (node == NULL)
260 return 0;
261 else
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)). */
272 static void
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 N ints in DATA, that its
285 structure is correct, and that certain operations on BT
286 produce the expected results. */
287 static void
288 check_bt (struct bt *bt, const int data[], size_t n)
290 struct element e;
291 size_t i;
292 int *order;
294 order = xmemdup (data, n * sizeof *data);
295 qsort (order, n, sizeof *order, compare_ints_noaux);
297 for (i = 0; i < n; i++)
299 struct bt_node *p;
301 e.data = data[i];
302 if (rand () % 2)
303 p = bt_find (bt, &e.node);
304 else
305 p = bt_insert (bt, &e.node);
306 check (p != NULL);
307 check (p != &e.node);
308 check (bt_node_to_element (p)->data == data[i]);
311 e.data = -1;
312 check (bt_find (bt, &e.node) == NULL);
314 check_balance (bt);
316 if (n == 0)
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);
323 else
325 struct bt_node *p;
327 for (p = bt_first (bt), i = 0; i < n; p = bt_next (bt, p), i++)
328 check (bt_node_to_element (p)->data == order[i]);
329 check (p == NULL);
331 for (p = bt_last (bt), i = 0; i < n; p = bt_prev (bt, p), i++)
332 check (bt_node_to_element (p)->data == order[n - i - 1]);
333 check (p == NULL);
336 free (order);
339 /* Inserts the N values from 0 to N - 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. */
343 static void
344 test_insert_delete (const int insertions[],
345 const int deletions[],
346 size_t n)
348 struct element *elements;
349 struct bt bt;
350 size_t i;
352 elements = xnmalloc (n, sizeof *elements);
353 for (i = 0; i < n; 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 < n; i++)
360 check (bt_insert (&bt, &elements[insertions[i]].node) == NULL);
361 check_bt (&bt, insertions, i + 1);
363 for (i = 0; i < n; i++)
365 bt_delete (&bt, &elements[deletions[i]].node);
366 check_bt (&bt, deletions + i + 1, n - i - 1);
369 free (elements);
372 /* Inserts values into an BT in each possible order, then
373 removes them in each possible order, up to a specified maximum
374 size. */
375 static void
376 test_insert_any_remove_any (void)
378 const int max_elems = 5;
379 int n;
381 for (n = 0; n <= max_elems; n++)
383 int *insertions, *deletions;
384 unsigned int ins_n_perms;
385 int i;
387 insertions = xnmalloc (n, sizeof *insertions);
388 deletions = xnmalloc (n, sizeof *deletions);
389 for (i = 0; i < n; i++)
390 insertions[i] = i;
392 for (ins_n_perms = 0;
393 ins_n_perms == 0 || next_permutation (insertions, n);
394 ins_n_perms++)
396 unsigned int del_n_perms;
397 int i;
399 for (i = 0; i < n; i++)
400 deletions[i] = i;
402 for (del_n_perms = 0;
403 del_n_perms == 0 || next_permutation (deletions, n);
404 del_n_perms++)
405 test_insert_delete (insertions, deletions, n);
407 check (del_n_perms == factorial (n));
409 check (ins_n_perms == factorial (n));
411 free (insertions);
412 free (deletions);
416 /* Inserts values into an BT in each possible order, then
417 removes them in the same order, up to a specified maximum
418 size. */
419 static void
420 test_insert_any_remove_same (void)
422 const int max_elems = 7;
423 int n;
425 for (n = 0; n <= max_elems; n++)
427 int *values;
428 unsigned int n_permutations;
429 int i;
431 values = xnmalloc (n, sizeof *values);
432 for (i = 0; i < n; i++)
433 values[i] = i;
435 for (n_permutations = 0;
436 n_permutations == 0 || next_permutation (values, n);
437 n_permutations++)
438 test_insert_delete (values, values, n);
439 check (n_permutations == factorial (n));
441 free (values);
445 /* Inserts values into an BT in each possible order, then
446 removes them in reverse order, up to a specified maximum
447 size. */
448 static void
449 test_insert_any_remove_reverse (void)
451 const int max_elems = 7;
452 int n;
454 for (n = 0; n <= max_elems; n++)
456 int *insertions, *deletions;
457 unsigned int n_permutations;
458 int i;
460 insertions = xnmalloc (n, sizeof *insertions);
461 deletions = xnmalloc (n, sizeof *deletions);
462 for (i = 0; i < n; i++)
463 insertions[i] = i;
465 for (n_permutations = 0;
466 n_permutations == 0 || next_permutation (insertions, n);
467 n_permutations++)
469 memcpy (deletions, insertions, sizeof *insertions * n);
470 reverse (deletions, n);
472 test_insert_delete (insertions, deletions, n);
474 check (n_permutations == factorial (n));
476 free (insertions);
477 free (deletions);
481 /* Inserts and removes values in an BT in random orders. */
482 static void
483 test_random_sequence (void)
485 const int max_elems = 128;
486 const int max_trials = 8;
487 int n;
489 for (n = 0; n <= max_elems; n += 2)
491 int *insertions, *deletions;
492 int trial;
493 int i;
495 insertions = xnmalloc (n, sizeof *insertions);
496 deletions = xnmalloc (n, sizeof *deletions);
497 for (i = 0; i < n; i++)
498 insertions[i] = i;
499 for (i = 0; i < n; i++)
500 deletions[i] = i;
502 for (trial = 0; trial < max_trials; trial++)
504 random_shuffle (insertions, n, sizeof *insertions);
505 random_shuffle (deletions, n, sizeof *deletions);
507 test_insert_delete (insertions, deletions, n);
510 free (insertions);
511 free (deletions);
515 /* Inserts elements into an BT in ascending order. */
516 static void
517 test_insert_ordered (void)
519 const int max_elems = 1024;
520 struct element *elements;
521 int *values;
522 struct bt bt;
523 int i;
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);
534 free (elements);
535 free (values);
538 /* Tests bt_find_ge and bt_find_le. */
539 static void
540 test_find_ge_le (void)
542 const int max_elems = 10;
543 struct element *elements;
544 int *values;
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++)
551 struct bt bt;
552 int n_elems = 0;
553 int i;
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[n_elems] = elements[n_elems].data = i;
561 check (bt_insert (&bt, &elements[n_elems].node) == NULL);
562 n_elems++;
564 check_bt (&bt, values, n_elems);
566 /* Try find_ge and find_le for each possible element value. */
567 for (i = -1; i <= max_elems; i++)
569 struct element tmp;
570 struct bt_node *ge, *le;
571 int j;
573 ge = le = NULL;
574 for (j = 0; j < n_elems; j++)
576 if (ge == NULL && values[j] >= i)
577 ge = &elements[j].node;
578 if (values[j] <= i)
579 le = &elements[j].node;
582 tmp.data = i;
583 check (bt_find_ge (&bt, &tmp.node) == ge);
584 check (bt_find_le (&bt, &tmp.node) == le);
587 free (elements);
588 free (values);
591 /* Inserts elements into an BT, then moves the nodes around in
592 memory. */
593 static void
594 test_moved (void)
596 const int max_elems = 128;
597 struct element *e[2];
598 int cur;
599 int *values;
600 struct bt bt;
601 int i, j;
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);
607 cur = 0;
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);
620 cur = !cur;
622 free (e[0]);
623 free (e[1]);
624 free (values);
627 /* Inserts values into an BT, then changes their values. */
628 static void
629 test_changed (void)
631 const int max_elems = 6;
632 int n;
634 for (n = 0; n <= max_elems; n++)
636 int *values, *changed_values;
637 struct element *elements;
638 unsigned int n_permutations;
639 int i;
641 values = xnmalloc (n, sizeof *values);
642 changed_values = xnmalloc (n, sizeof *changed_values);
643 elements = xnmalloc (n, sizeof *elements);
644 for (i = 0; i < n; i++)
645 values[i] = i;
647 for (n_permutations = 0;
648 n_permutations == 0 || next_permutation (values, n);
649 n_permutations++)
651 for (i = 0; i < n; i++)
653 int j, k;
654 for (j = 0; j <= n; j++)
656 struct bt bt;
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 < n; k++)
664 int n = values[k];
665 elements[n].data = n;
666 check (bt_insert (&bt, &elements[n].node) == NULL);
668 check_bt (&bt, values, n);
670 /* Change value i to j. */
671 elements[i].data = j;
672 for (k = 0; k < n; k++)
673 changed_values[k] = k;
674 changed_retval = bt_changed (&bt, &elements[i].node);
675 if (i != j && j < n)
677 /* Will cause duplicate. */
678 check (changed_retval == &elements[j].node);
679 changed_values[i] = changed_values[n - 1];
680 check_bt (&bt, changed_values, n - 1);
682 else
684 /* Succeeds. */
685 check (changed_retval == NULL);
686 changed_values[i] = j;
687 check_bt (&bt, changed_values, n);
692 check (n_permutations == factorial (n));
694 free (values);
695 free (changed_values);
696 free (elements);
700 /* Main program. */
702 struct test
704 const char *name;
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
727 "random-sequence",
728 "insert and delete in random sequence",
729 test_random_sequence
732 "insert-ordered",
733 "insert in ascending order",
734 test_insert_ordered
737 "find-ge-le",
738 "find_ge and find_le",
739 test_find_ge_le
742 "moved",
743 "move elements around in memory",
744 test_moved
747 "changed",
748 "change key data in nodes",
749 test_changed
753 enum { N_TESTS = sizeof tests / sizeof *tests };
756 main (int argc, char *argv[])
758 int i;
760 if (argc != 2)
762 fprintf (stderr, "exactly one argument required; use --help for help\n");
763 return EXIT_FAILURE;
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",
770 argv[0], argv[0]);
771 for (i = 0; i < N_TESTS; i++)
772 printf (" %s\n %s\n", tests[i].name, tests[i].description);
773 return 0;
775 else
777 for (i = 0; i < N_TESTS; i++)
778 if (!strcmp (argv[1], tests[i].name))
780 tests[i].function ();
781 return 0;
784 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);
785 return EXIT_FAILURE;