From: aisha Date: Fri, 29 Dec 2023 02:37:39 +0000 (+0000) Subject: more regress tests for RB_* and RBT_* macros X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=5dada7d42bfa0d3cef60543f50988cc714f0e977;p=openbsd more regress tests for RB_* and RBT_* macros help, pointers and OK bluhm@ --- diff --git a/regress/sys/sys/tree/rb/Makefile b/regress/sys/sys/tree/rb/Makefile index 61695ac2259..1619698905a 100644 --- a/regress/sys/sys/tree/rb/Makefile +++ b/regress/sys/sys/tree/rb/Makefile @@ -1,5 +1,24 @@ -# $OpenBSD: Makefile,v 1.1 2002/06/11 22:10:22 provos Exp $ +# $OpenBSD: Makefile,v 1.2 2023/12/29 02:37:39 aisha Exp $ -PROG= rb-test +NAMED_TESTS = random-inserts sequential-inserts \ + sequential-removes random-removes remove-nfind \ + remove-pfind node-iterations iteration-macros insert-next \ + insert-prev benchmarks + +REGRESS_TARGETS = all-tests ${NAMED_TESTS} + +PROGS = rb-test rbt-test + +all-tests: rb-test rbt-test + @echo "===== testing RB macros =====" + ./rb-test + @echo "===== testing RBT macros =====" + ./rbt-test + +${NAMED_TESTS}: rb-test rbt-test + @echo "===== testing RB macros =====" + ./rb-test $@ + @echo "===== testing RBT macros =====" + ./rbt-test $@ .include diff --git a/regress/sys/sys/tree/rb/rb-test.c b/regress/sys/sys/tree/rb/rb-test.c index 409cc22393a..c26b549d588 100644 --- a/regress/sys/sys/tree/rb/rb-test.c +++ b/regress/sys/sys/tree/rb/rb-test.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rb-test.c,v 1.4 2008/04/13 00:22:17 djm Exp $ */ +/* $OpenBSD: rb-test.c,v 1.5 2023/12/29 02:37:39 aisha Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. @@ -25,82 +25,952 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include +#include + +#include #include +#include #include +#include +#include +#include + +struct timespec start, end, diff, rstart, rend, rdiff, rtot = {0, 0}; +#ifndef timespecsub +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (0) +#endif +#ifndef timespecadd +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (0) +#endif + +//#define RB_SMALL +//#define RB_TEST_RANK +//#define RB_TEST_DIAGNOSTIC +//#define _RB_DIAGNOSTIC + +#ifdef DOAUGMENT +#define RB_AUGMENT(elm) tree_augment(elm) +#endif + +#include + +#define TDEBUGF(fmt, ...) \ + fprintf(stderr, "%s:%d:%s(): " fmt "\n", \ + __FILE__, __LINE__, __func__, ##__VA_ARGS__) + +#ifdef __OpenBSD__ +#define SEED_RANDOM srandom_deterministic +#else +#define SEED_RANDOM srandom +#endif + +int ITER=150000; +int RANK_TEST_ITERATIONS=10000; + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +/* declarations */ +struct node; +struct tree; +static int compare(const struct node *, const struct node *); +static void mix_operations(int *, int, struct node *, int, int, int, int); + +#ifdef DOAUGMENT +static int tree_augment(struct node *); +#else +#define tree_augment(x) (0) +#endif + +#ifdef RB_TEST_DIAGNOSTIC +static void print_helper(const struct node *, int); +static void print_tree(const struct tree *); +#else +#define print_helper(x, y) do {} while (0) +#define print_tree(x) do {} while (0) +#endif + +/* definitions */ struct node { - RB_ENTRY(node) node; - int key; + RB_ENTRY(node) node_link; + int key; + size_t height; + size_t size; }; -RB_HEAD(tree, node) root; +RB_HEAD(tree, node); +struct tree root = RB_INITIALIZER(&root); -static int -compare(struct node *a, struct node *b) -{ - if (a->key < b->key) return (-1); - else if (a->key > b->key) return (1); - return (0); -} +RB_PROTOTYPE(tree, node, node_link, compare) -RB_PROTOTYPE(tree, node, node, compare); +RB_GENERATE(tree, node, node_link, compare) -RB_GENERATE(tree, node, node, compare); - -#define ITER 150 -#define MIN 5 -#define MAX 5000 +#ifndef RB_RANK +#define RB_RANK(x, y) 0 +#endif +#ifndef _RB_GET_RDIFF +#define _RB_GET_RDIFF(x, y, z) 0 +#endif int main(int argc, char **argv) { - struct node *tmp, *ins; - int i, max, min; + char *test_target = NULL; + struct node *tmp, *ins, *nodes; + int i, r, rank, *perm, *nums; + + if (argc > 1) + test_target = argv[1]; + + nodes = calloc((ITER + 5), sizeof(struct node)); + perm = calloc(ITER, sizeof(int)); + nums = calloc(ITER, sizeof(int)); + + // for determinism + SEED_RANDOM(4201); + + TDEBUGF("generating a 'random' permutation"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + perm[0] = 0; + nums[0] = 0; + for(i = 1; i < ITER; i++) { + r = random() % i; // arc4random_uniform(i); + perm[i] = perm[r]; + perm[r] = i; + nums[i] = i; + } + /* + fprintf(stderr, "{"); + for(int i = 0; i < ITER; i++) { + fprintf(stderr, "%d, ", perm[i]); + } + fprintf(stderr, "}\n"); + int nperm[10] = {2, 4, 9, 7, 8, 3, 0, 1, 6, 5}; + int nperm[6] = {2, 6, 1, 4, 5, 3}; + int nperm[10] = {10, 3, 7, 8, 6, 1, 9, 2, 5, 4}; + int nperm[2] = {0, 1}; + + int nperm[100] = { + 54, 47, 31, 35, 40, 73, 29, 66, 15, 45, 9, 71, 51, 32, 28, 62, + 12, 46, 50, 26, 36, 91, 10, 76, 33, 43, 34, 58, 55, 72, 37, 24, + 75, 4, 90, 88, 30, 25, 82, 18, 67, 81, 80, 65, 23, 41, 61, 86, + 20, 99, 59, 14, 79, 21, 68, 27, 1, 7, 94, 44, 89, 64, 96, 2, 49, + 53, 74, 13, 48, 42, 60, 52, 95, 17, 11, 0, 22, 97, 77, 69, 6, + 16, 84, 78, 8, 83, 98, 93, 39, 38, 85, 70, 3, 19, 57, 5, 87, + 92, 63, 56 + }; + ITER = 100; + for(int i = 0; i < ITER; i++){ + perm[i] = nperm[i]; + } + */ + + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done generating a 'random' permutation in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); RB_INIT(&root); - for (i = 0; i < ITER; i++) { + // testing random inserts + // due to codependency between inserts and removals, this also tests + // root removals + if (test_target == NULL || + strcmp(test_target, "random-inserts") == 0 + ) { + TDEBUGF("starting random insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done random insertions in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + +#ifdef DOAUGMENT + ins = RB_ROOT(&root); + assert(ITER + 1 == ins->size); +#endif + + TDEBUGF("getting min"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + ins = RB_MIN(tree, &root); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done getting min in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + assert(0 == ins->key); + + TDEBUGF("getting max"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + ins = RB_MAX(tree, &root); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done getting max in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + assert(ITER + 5 == ins->key); + + ins = RB_ROOT(&root); + TDEBUGF("getting root"); + assert(RB_REMOVE(tree, &root, ins) == ins); + +#ifdef DOAUGMENT + assert(ITER == (RB_ROOT(&root))->size); +#endif + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER; i++) { + tmp = RB_ROOT(&root); + assert(NULL != tmp); + assert(RB_REMOVE(tree, &root, tmp) == tmp); +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + assert(-2 != rank); + print_tree(&root); + } +#endif + +#ifdef DOAUGMENT + if (!(RB_EMPTY(&root)) && (RB_ROOT(&root))->size != ITER - 1 - i) + errx(1, "RB_REMOVE size error"); +#endif + + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + + // testing insertions in increasing sequential order + // removals are done using root removals (separate test) + if (test_target == NULL || + strcmp(test_target, "sequential-inserts") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER + 1; i++) { + tmp = RB_ROOT(&root); + assert(NULL != tmp); + assert(RB_REMOVE(tree, &root, tmp) == tmp); +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + + // testing the RB_FIND function and using it to do removals in + // sequential order + // insertions are done using sequential insertions (separate test) + if (test_target == NULL || + strcmp(test_target, "sequential-removes") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing find and remove in sequential order"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); tmp = malloc(sizeof(struct node)); - if (tmp == NULL) err(1, "malloc"); - do { - tmp->key = arc4random_uniform(MAX-MIN); - tmp->key += MIN; - } while (RB_FIND(tree, &root, tmp) != NULL); - if (i == 0) - max = min = tmp->key; - else { - if (tmp->key > max) - max = tmp->key; - if (tmp->key < min) - min = tmp->key; + for(i = 0; i < ITER; i++) { + tmp->key = i; + ins = RB_FIND(tree, &root, tmp); + assert(NULL != tmp); + assert(RB_REMOVE(tree, &root, ins) == ins); +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif } - if (RB_INSERT(tree, &root, tmp) != NULL) - errx(1, "RB_INSERT failed"); + free(tmp); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done removals in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + ins = RB_ROOT(&root); + if (ins == NULL) + errx(1, "RB_ROOT error"); + if (RB_REMOVE(tree, &root, ins) != ins) + errx(1, "RB_REMOVE failed"); } - ins = RB_MIN(tree, &root); - if (ins->key != min) - errx(1, "min does not match"); - tmp = ins; - ins = RB_MAX(tree, &root); - if (ins->key != max) - errx(1, "max does not match"); + // testing removals in random order + // the elements are found using RB_FIND + // insertions are done using sequential insertions (separate test) + if (test_target == NULL || + strcmp(test_target, "random-removes") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); - if (RB_REMOVE(tree, &root, tmp) != tmp) - errx(1, "RB_REMOVE failed"); - for (i = 0; i < ITER - 1; i++) { - tmp = RB_ROOT(&root); - if (tmp == NULL) + TDEBUGF("doing find and remove in random order"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = malloc(sizeof(struct node)); + for(i = 0; i < ITER; i++) { + tmp->key = perm[i]; + ins = RB_FIND(tree, &root, tmp); + if (ins == NULL) { + errx(1, "RB_FIND %d failed: %d", i, perm[i]); + } + if (RB_REMOVE(tree, &root, ins) == NULL) + errx(1, "RB_REMOVE failed: %d", i); +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + free(tmp); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done removals in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + ins = RB_ROOT(&root); + if (ins == NULL) errx(1, "RB_ROOT error"); - if (RB_REMOVE(tree, &root, tmp) != tmp) - errx(1, "RB_REMOVE error"); + if (RB_REMOVE(tree, &root, ins) != ins) + errx(1, "RB_REMOVE failed"); + } + + // testing removals by finding the next largest element + // this is testing the RB_NFIND macro + // insertions are done using sequential insertions (separate test) + if (test_target == NULL || + strcmp(test_target, "remove-nfind") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing nfind and remove"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = malloc(sizeof(struct node)); + for(i = 0; i < ITER + 1; i++) { + tmp->key = i; + ins = RB_NFIND(tree, &root, tmp); + if (ins == NULL) + errx(1, "RB_NFIND failed"); + if (RB_REMOVE(tree, &root, ins) == NULL) + errx(1, "RB_REMOVE failed: %d", i); +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + free(tmp); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done removals in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + +#ifdef RB_PFIND + // testing removals by finding the previous element + // this is testing the RB_PFIND macro + // insertions are done using sequential insertions (separate test) + if (test_target == NULL || + strcmp(test_target, "remove-pfind") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing pfind and remove"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = malloc(sizeof(struct node)); + for(i = 0; i < ITER + 1; i++) { + tmp->key = ITER + 6; + ins = RB_PFIND(tree, &root, tmp); + if (ins == NULL) + errx(1, "RB_PFIND failed"); + if (RB_REMOVE(tree, &root, ins) == NULL) + errx(1, "RB_REMOVE failed: %d", i); +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } free(tmp); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done removals in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } +#endif + + // iterate over the tree using RB_NEXT/RB_PREV + // insertions and removals have separate tests + if (test_target == NULL || + strcmp(test_target, "node-iterations") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree with RB_NEXT"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = RB_MIN(tree, &root); + assert(tmp != NULL); + assert(tmp->key == 0); + for(i = 1; i < ITER; i++) { + tmp = RB_NEXT(tree, &root, tmp); + assert(tmp != NULL); + assert(tmp->key == i); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree with RB_PREV"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = RB_MAX(tree, &root); + assert(tmp != NULL); + assert(tmp->key == ITER + 5); + for(i = 0; i < ITER; i++) { + tmp = RB_PREV(tree, &root, tmp); + assert(tmp != NULL); + assert(tmp->key == ITER - 1 - i); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER + 1; i++) { + tmp = RB_ROOT(&root); + assert(NULL != tmp); + assert(RB_REMOVE(tree, &root, tmp) == tmp); +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + + // iterate over the tree using RB_FOREACH* macros + // the *_SAFE macros are tested by using them to clear the tree + // insertions and removals have separate tests + if (test_target == NULL || + strcmp(test_target, "iteration-macros") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + +#ifdef RB_FOREACH + TDEBUGF("iterating over tree with RB_FOREACH"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + i = 0; + RB_FOREACH(ins, tree, &root) { + if (i < ITER) + assert(ins->key == i); + else + assert(ins->key == ITER + 5); + i++; + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); +#endif + +#ifdef RB_FOREACH_REVERSE + TDEBUGF("iterating over tree with RB_FOREACH_REVERSE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + i = ITER + 5; + RB_FOREACH_REVERSE(ins, tree, &root) { + assert(ins->key == i); + if (i > ITER) + i = ITER - 1; + else + i--; + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); +#endif + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER + 1; i++) { + tmp = RB_ROOT(&root); + assert(NULL != tmp); + assert(RB_REMOVE(tree, &root, tmp) == tmp); +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + +#ifdef RB_FOREACH_SAFE + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree and clearing with RB_FOREACH_SAFE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + i = 0; + RB_FOREACH_SAFE(ins, tree, &root, tmp) { + if (i < ITER) + assert(ins->key == i); + else + assert(ins->key == ITER + 5); + i++; + assert(RB_REMOVE(tree, &root, ins) == ins); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); +#endif + +#ifdef RB_FOREACH_REVERSE_SAFE + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree and clearing with RB_FOREACH_REVERSE_SAFE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + i = ITER + 5; + RB_FOREACH_REVERSE_SAFE(ins, tree, &root, tmp) { + assert(ins->key == i); + if (i > ITER) + i = ITER - 1; + else + i--; + assert(RB_REMOVE(tree, &root, ins) == ins); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); +#endif + } + +#ifdef RB_INSERT_NEXT + // testing insertions at specific points in the tree + // the insertions are done at the next position in the tree at a give node + // this assumes that the location is correct for the given ordering + if (test_target == NULL || + strcmp(test_target, "insert-next") == 0 + ) { + TDEBUGF("starting sequential insertions using INSERT_NEXT"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = &(nodes[0]); + tmp->size = 1; + tmp->height = 1; + tmp->key = 0; + if (RB_INSERT(tree, &root, tmp) != NULL) + errx(1, "RB_INSERT failed"); + ins = tmp; + for(i = 1; i < ITER; i++) { + tmp = &(nodes[i]); + tmp->size = 1; + tmp->height = 1; + tmp->key = i; + if (RB_INSERT_NEXT(tree, &root, ins, tmp) != NULL) + errx(1, "RB_INSERT failed"); + ins = tmp; +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done insertions in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree and clearing with RB_FOREACH_REVERSE_SAFE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + RB_FOREACH_REVERSE_SAFE(ins, tree, &root, tmp) { + assert(RB_REMOVE(tree, &root, ins) == ins); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); } +#endif + +#ifdef RB_INSERT_PREV + // testing insertions at specific points in the tree + // the insertions are done at the next position in the tree at a give node + // this assumes that the location is correct for the given ordering + if (test_target == NULL || + strcmp(test_target, "insert-prev") == 0 + ) { + TDEBUGF("starting sequential insertions using INSERT_PREV"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = &(nodes[ITER]); + tmp->size = 1; + tmp->height = 1; + tmp->key = ITER; + if (RB_INSERT(tree, &root, tmp) != NULL) + errx(1, "RB_INSERT failed"); + ins = tmp; + for(i = ITER - 1; i >= 0; i--) { + tmp = &(nodes[i]); + tmp->size = 1; + tmp->height = 1; + tmp->key = i; + if (RB_INSERT_PREV(tree, &root, ins, tmp) != NULL) + errx(1, "RB_INSERT failed"); + ins = tmp; +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done insertions in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree and clearing with RB_FOREACH_REVERSE_SAFE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + RB_FOREACH_REVERSE_SAFE(ins, tree, &root, tmp) { + assert(RB_REMOVE(tree, &root, ins) == ins); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } +#endif + + if (test_target == NULL || + strcmp(test_target, "benchmarks") == 0 + ) { + TDEBUGF("doing 50%% insertions, 50%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, ITER / 2, ITER / 2, 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER / 2 + 1; i++) { + tmp = RB_ROOT(&root); + if (tmp == NULL) + errx(1, "RB_ROOT error"); + if (RB_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RB_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing 20%% insertions, 80%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, ITER / 5, 4 * (ITER / 5), 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER / 5 + 1; i++) { + tmp = RB_ROOT(&root); + if (tmp == NULL) + errx(1, "RB_ROOT error"); + if (RB_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RB_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing 10%% insertions, 90%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, ITER / 10, 9 * (ITER / 10), 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER / 10 + 1; i++) { + tmp = RB_ROOT(&root); + if (tmp == NULL) + errx(1, "RB_ROOT error"); + if (RB_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RB_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing 5%% insertions, 95%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, + ITER, 5 * (ITER / 100), + 95 * (ITER / 100), 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < 5 * (ITER / 100) + 1; i++) { + tmp = RB_ROOT(&root); + if (tmp == NULL) + errx(1, "RB_ROOT error"); + if (RB_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RB_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); - exit(0); + TDEBUGF("doing 2%% insertions, 98%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, + 2 * (ITER / 100), + 98 * (ITER / 100), 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < 2 * (ITER / 100) + 1; i++) { + tmp = RB_ROOT(&root); + if (tmp == NULL) + errx(1, "RB_ROOT error"); + if (RB_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RB_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + + free(nodes); + free(perm); + free(nums); + + return 0; +} + + +static int +compare(const struct node *a, const struct node *b) +{ + return a->key - b->key; +} + +#ifdef RB_TEST_DIAGNOSTIC +static void +print_helper(const struct node *n, int indent) +{ + if (RB_RIGHT(n, node_link)) + print_helper(RB_RIGHT(n, node_link), indent + 4); + TDEBUGF("%*s key=%d :: size=%zu :: rank=%d :: rdiff %lu:%lu", + indent, "", n->key, n->size, RB_RANK(tree, n), + _RB_GET_RDIFF(n, _RB_LDIR, node_link), + _RB_GET_RDIFF(n, _RB_RDIR, node_link)); + if (RB_LEFT(n, node_link)) + print_helper(RB_LEFT(n, node_link), indent + 4); +} + +static void +print_tree(const struct tree *t) +{ + if (RB_ROOT(t)) print_helper(RB_ROOT(t), 0); +} +#endif + +#ifdef DOAUGMENT +static int +tree_augment(struct node *elm) +{ + size_t newsize = 1, newheight = 0; + if ((RB_LEFT(elm, node_link))) { + newsize += (RB_LEFT(elm, node_link))->size; + newheight = MAX((RB_LEFT(elm, node_link))->height, newheight); + } + if ((RB_RIGHT(elm, node_link))) { + newsize += (RB_RIGHT(elm, node_link))->size; + newheight = MAX((RB_RIGHT(elm, node_link))->height, newheight); + } + newheight += 1; + if (elm->size != newsize || elm->height != newheight) { + elm->size = newsize; + elm->height = newheight; + return 1; + } + return 0; +} +#endif + + +void +mix_operations(int *perm, int psize, struct node *nodes, int nsize, + int insertions, int reads, int do_reads) +{ + int i, rank; + struct node *tmp, *ins; + struct node it; + assert(psize == nsize); + assert(insertions + reads <= psize); + + for(i = 0; i < insertions; i++) { + //TDEBUGF("iteration %d", i); + tmp = &(nodes[i]); + if (tmp == NULL) err(1, "malloc"); + tmp->size = 1; + tmp->height = 1; + tmp->key = perm[i]; + //TDEBUGF("inserting %d", tmp->key); + if (RB_INSERT(tree, &root, tmp) != NULL) + errx(1, "RB_INSERT failed"); + print_tree(&root); +#ifdef DOAUGMENT + //TDEBUGF("size = %zu", RB_ROOT(&root)->size); + assert(RB_ROOT(&root)->size == i + 1); +#endif + +#ifdef RB_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RB_RANK(tree, RB_ROOT(&root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + tmp = &(nodes[insertions]); + tmp->key = ITER + 5; + tmp->size = 1; + tmp->height = 1; + RB_INSERT(tree, &root, tmp); + if (do_reads) { + for (i = 0; i < insertions; i++) { + it.key = perm[i]; + ins = RB_FIND(tree, &root, &it); + if ((ins == NULL) || ins->key != it.key) + errx(1, "RB_FIND failed"); + } + for (i = insertions; i < insertions + reads; i++) { + it.key = perm[i]; + ins = RB_NFIND(tree, &root, &it); + if (ins->key < it.key) + errx(1, "RB_NFIND failed"); + } + } } diff --git a/regress/sys/sys/tree/rb/rbt-test.c b/regress/sys/sys/tree/rb/rbt-test.c new file mode 100644 index 00000000000..006f3d3f738 --- /dev/null +++ b/regress/sys/sys/tree/rb/rbt-test.c @@ -0,0 +1,975 @@ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include +#include +#include +#include +#include +#include +#include + +struct timespec start, end, diff, rstart, rend, rdiff, rtot = {0, 0}; +#ifndef timespecsub +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (0) +#endif +#ifndef timespecadd +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (0) +#endif + +//#define RBT_SMALL +//#define RBT_TEST_RANK +//#define RBT_TEST_DIAGNOSTIC +//#define _RBT_DIAGNOSTIC + +#ifdef DOAUGMENT +#define RBT_AUGMENT(elm) tree_augment(elm) +#endif + +#include + +#define TDEBUGF(fmt, ...) \ + fprintf(stderr, "%s:%d:%s(): " fmt "\n", \ + __FILE__, __LINE__, __func__, ##__VA_ARGS__) + + +#ifdef __OpenBSD__ +#define SEED_RANDOM srandom_deterministic +#else +#define SEED_RANDOM srandom +#endif + +int ITER=150000; +int RANK_TEST_ITERATIONS=10000; + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +/* declarations */ +struct node; +struct tree; +static int compare(const struct node *, const struct node *); +static void mix_operations(int *, int, struct node *, int, int, int, int); + +#ifdef DOAUGMENT +static int tree_augment(struct node *); +#else +#define tree_augment(x) (0) +#endif + +#ifdef RBT_TEST_DIAGNOSTIC +static void print_helper(const struct node *, int); +static void print_tree(const struct tree *); +#else +#define print_helper(x, y) do {} while (0) +#define print_tree(x) do {} while (0) +#endif + +/* definitions */ +struct node { + RBT_ENTRY(node) node_link; + int key; + size_t height; + size_t size; +}; + +RBT_HEAD(tree, node); +struct tree root = RBT_INITIALIZER(&root); + +RBT_PROTOTYPE(tree, node, node_link, compare) + +RBT_GENERATE(tree, node, node_link, compare); + +#ifndef RBT_RANK +#define RBT_RANK(x, y) 0 +#endif +#ifndef _RBT_GET_RDIFF +#define _RBT_GET_RDIFF(x, y, z) 0 +#endif + +int +main(int argc, char **argv) +{ + char *test_target = NULL; + struct node *tmp, *ins, *nodes; + int i, r, rank, *perm, *nums; + + if (argc > 1) + test_target = argv[1]; + + nodes = calloc((ITER + 5), sizeof(struct node)); + perm = calloc(ITER, sizeof(int)); + nums = calloc(ITER, sizeof(int)); + + // for determinism + SEED_RANDOM(4201); + + TDEBUGF("generating a 'random' permutation"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + perm[0] = 0; + nums[0] = 0; + for(i = 1; i < ITER; i++) { + r = random() % i; // arc4random_uniform(i); + perm[i] = perm[r]; + perm[r] = i; + nums[i] = i; + } + /* + fprintf(stderr, "{"); + for(int i = 0; i < ITER; i++) { + fprintf(stderr, "%d, ", perm[i]); + } + fprintf(stderr, "}\n"); + int nperm[10] = {2, 4, 9, 7, 8, 3, 0, 1, 6, 5}; + int nperm[6] = {2, 6, 1, 4, 5, 3}; + int nperm[10] = {10, 3, 7, 8, 6, 1, 9, 2, 5, 4}; + int nperm[2] = {0, 1}; + + int nperm[100] = { + 54, 47, 31, 35, 40, 73, 29, 66, 15, 45, 9, 71, 51, 32, 28, 62, + 12, 46, 50, 26, 36, 91, 10, 76, 33, 43, 34, 58, 55, 72, 37, 24, + 75, 4, 90, 88, 30, 25, 82, 18, 67, 81, 80, 65, 23, 41, 61, 86, + 20, 99, 59, 14, 79, 21, 68, 27, 1, 7, 94, 44, 89, 64, 96, 2, 49, + 53, 74, 13, 48, 42, 60, 52, 95, 17, 11, 0, 22, 97, 77, 69, 6, + 16, 84, 78, 8, 83, 98, 93, 39, 38, 85, 70, 3, 19, 57, 5, 87, + 92, 63, 56 + }; + ITER = 100; + for(int i = 0; i < ITER; i++){ + perm[i] = nperm[i]; + } + */ + + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done generating a 'random' permutation in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + RBT_INIT(tree, &root); + + // testing random inserts + // due to codependency between inserts and removals, this also tests + // root removals + if (test_target == NULL || + strcmp(test_target, "random-inserts") == 0 + ) { + TDEBUGF("starting random insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done random insertions in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + +#ifdef DOAUGMENT + ins = RBT_ROOT(tree, &root); + assert(ITER + 1 == ins->size); +#endif + + TDEBUGF("getting min"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + ins = RBT_MIN(tree, &root); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done getting min in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + assert(0 == ins->key); + + TDEBUGF("getting max"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + ins = RBT_MAX(tree, &root); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done getting max in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + assert(ITER + 5 == ins->key); + + ins = RBT_ROOT(tree, &root); + TDEBUGF("getting root"); + assert(RBT_REMOVE(tree, &root, ins) == ins); + +#ifdef DOAUGMENT + assert(ITER == (RBT_ROOT(tree, &root))->size); +#endif + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER; i++) { + tmp = RBT_ROOT(tree, &root); + assert(NULL != tmp); + assert(RBT_REMOVE(tree, &root, tmp) == tmp); +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + assert(-2 != rank); + print_tree(&root); + } +#endif + +#ifdef DOAUGMENT + if (!(RBT_EMPTY(&root)) && (RBT_ROOT(tree, &root))->size != ITER - 1 - i) + errx(1, "RBT_REMOVE size error"); +#endif + + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + + // testing insertions in increasing sequential order + // removals are done using root removals (separate test) + if (test_target == NULL || + strcmp(test_target, "sequential-inserts") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER + 1; i++) { + tmp = RBT_ROOT(tree, &root); + assert(NULL != tmp); + assert(RBT_REMOVE(tree, &root, tmp) == tmp); +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + + // testing the RBT_FIND function and using it to do removals in + // sequential order + // insertions are done using sequential insertions (separate test) + if (test_target == NULL || + strcmp(test_target, "sequential-removes") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing find and remove in sequential order"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = malloc(sizeof(struct node)); + for(i = 0; i < ITER; i++) { + tmp->key = i; + ins = RBT_FIND(tree, &root, tmp); + assert(NULL != tmp); + assert(RBT_REMOVE(tree, &root, ins) == ins); +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + free(tmp); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done removals in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + ins = RBT_ROOT(tree, &root); + if (ins == NULL) + errx(1, "RBT_ROOT error"); + if (RBT_REMOVE(tree, &root, ins) != ins) + errx(1, "RBT_REMOVE failed"); + } + + // testing removals in random order + // the elements are found using RBT_FIND + // insertions are done using sequential insertions (separate test) + if (test_target == NULL || + strcmp(test_target, "random-removes") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + + TDEBUGF("doing find and remove in random order"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = malloc(sizeof(struct node)); + for(i = 0; i < ITER; i++) { + tmp->key = perm[i]; + ins = RBT_FIND(tree, &root, tmp); + if (ins == NULL) { + errx(1, "RBT_FIND %d failed: %d", i, perm[i]); + } + if (RBT_REMOVE(tree, &root, ins) == NULL) + errx(1, "RBT_REMOVE failed: %d", i); +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + free(tmp); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done removals in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + ins = RBT_ROOT(tree, &root); + if (ins == NULL) + errx(1, "RBT_ROOT error"); + if (RBT_REMOVE(tree, &root, ins) != ins) + errx(1, "RBT_REMOVE failed"); + } + + // testing removals by finding the next largest element + // this is testing the RBT_NFIND macro + // insertions are done using sequential insertions (separate test) + if (test_target == NULL || + strcmp(test_target, "remove-nfind") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing nfind and remove"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = malloc(sizeof(struct node)); + for(i = 0; i < ITER + 1; i++) { + tmp->key = i; + ins = RBT_NFIND(tree, &root, tmp); + if (ins == NULL) + errx(1, "RBT_NFIND failed"); + if (RBT_REMOVE(tree, &root, ins) == NULL) + errx(1, "RBT_REMOVE failed: %d", i); +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + free(tmp); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done removals in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + +#ifdef RBT_PFIND + // testing removals by finding the previous element + // this is testing the RBT_PFIND macro + // insertions are done using sequential insertions (separate test) + if (test_target == NULL || + strcmp(test_target, "remove-pfind") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing pfind and remove"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = malloc(sizeof(struct node)); + for(i = 0; i < ITER + 1; i++) { + tmp->key = ITER + 6; + ins = RBT_PFIND(tree, &root, tmp); + if (ins == NULL) + errx(1, "RBT_PFIND failed"); + if (RBT_REMOVE(tree, &root, ins) == NULL) + errx(1, "RBT_REMOVE failed: %d", i); +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + free(tmp); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done removals in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } +#endif + + // iterate over the tree using RBT_NEXT/RBT_PREV + // insertions and removals have separate tests + if (test_target == NULL || + strcmp(test_target, "node-iterations") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree with RBT_NEXT"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = RBT_MIN(tree, &root); + assert(tmp != NULL); + assert(tmp->key == 0); + for(i = 1; i < ITER; i++) { + tmp = RBT_NEXT(tree, tmp); + assert(tmp != NULL); + assert(tmp->key == i); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree with RBT_PREV"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = RBT_MAX(tree, &root); + assert(tmp != NULL); + assert(tmp->key == ITER + 5); + for(i = 0; i < ITER; i++) { + tmp = RBT_PREV(tree, tmp); + assert(tmp != NULL); + assert(tmp->key == ITER - 1 - i); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER + 1; i++) { + tmp = RBT_ROOT(tree, &root); + assert(NULL != tmp); + assert(RBT_REMOVE(tree, &root, tmp) == tmp); +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + + // iterate over the tree using RBT_FOREACH* macros + // the *_SAFE macros are tested by using them to clear the tree + // insertions and removals have separate tests + if (test_target == NULL || + strcmp(test_target, "iteration-macros") == 0 + ) { + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + +#ifdef RBT_FOREACH + TDEBUGF("iterating over tree with RBT_FOREACH"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + i = 0; + RBT_FOREACH(ins, tree, &root) { + if (i < ITER) + assert(ins->key == i); + else + assert(ins->key == ITER + 5); + i++; + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); +#endif + +#ifdef RBT_FOREACH_REVERSE + TDEBUGF("iterating over tree with RBT_FOREACH_REVERSE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + i = ITER + 5; + RBT_FOREACH_REVERSE(ins, tree, &root) { + assert(ins->key == i); + if (i > ITER) + i = ITER - 1; + else + i--; + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); +#endif + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER + 1; i++) { + tmp = RBT_ROOT(tree, &root); + assert(NULL != tmp); + assert(RBT_REMOVE(tree, &root, tmp) == tmp); +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + +#ifdef RBT_FOREACH_SAFE + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree and clearing with RBT_FOREACH_SAFE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + i = 0; + RBT_FOREACH_SAFE(ins, tree, &root, tmp) { + if (i < ITER) + assert(ins->key == i); + else + assert(ins->key == ITER + 5); + i++; + assert(RBT_REMOVE(tree, &root, ins) == ins); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); +#endif + +#ifdef RBT_FOREACH_REVERSE_SAFE + TDEBUGF("starting sequential insertions"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(nums, ITER, nodes, ITER, ITER, 0, 0); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done sequential insertions in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree and clearing with RBT_FOREACH_REVERSE_SAFE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + i = ITER + 5; + RBT_FOREACH_REVERSE_SAFE(ins, tree, &root, tmp) { + assert(ins->key == i); + if (i > ITER) + i = ITER - 1; + else + i--; + assert(RBT_REMOVE(tree, &root, ins) == ins); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); +#endif + } + +#ifdef RBT_INSERT_NEXT + // testing insertions at specific points in the tree + // the insertions are done at the next position in the tree at a give node + // this assumes that the location is correct for the given ordering + if (test_target == NULL || + strcmp(test_target, "insert-next") == 0 + ) { + TDEBUGF("starting sequential insertions using INSERT_NEXT"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = &(nodes[0]); + tmp->size = 1; + tmp->height = 1; + tmp->key = 0; + if (RBT_INSERT(tree, &root, tmp) != NULL) + errx(1, "RBT_INSERT failed"); + ins = tmp; + for(i = 1; i < ITER; i++) { + tmp = &(nodes[i]); + tmp->size = 1; + tmp->height = 1; + tmp->key = i; + if (RBT_INSERT_NEXT(tree, &root, ins, tmp) != NULL) + errx(1, "RBT_INSERT failed"); + ins = tmp; +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done insertions in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree and clearing with RBT_FOREACH_REVERSE_SAFE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + RBT_FOREACH_REVERSE_SAFE(ins, tree, &root, tmp) { + assert(RBT_REMOVE(tree, &root, ins) == ins); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } +#endif + +#ifdef RBT_INSERT_PREV + // testing insertions at specific points in the tree + // the insertions are done at the next position in the tree at a give node + // this assumes that the location is correct for the given ordering + if (test_target == NULL || + strcmp(test_target, "insert-prev") == 0 + ) { + TDEBUGF("starting sequential insertions using INSERT_PREV"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + tmp = &(nodes[ITER]); + tmp->size = 1; + tmp->height = 1; + tmp->key = ITER; + if (RBT_INSERT(tree, &root, tmp) != NULL) + errx(1, "RBT_INSERT failed"); + ins = tmp; + for(i = ITER - 1; i >= 0; i--) { + tmp = &(nodes[i]); + tmp->size = 1; + tmp->height = 1; + tmp->key = i; + if (RBT_INSERT_PREV(tree, &root, ins, tmp) != NULL) + errx(1, "RBT_INSERT failed"); + ins = tmp; +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done insertions in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("iterating over tree and clearing with RBT_FOREACH_REVERSE_SAFE"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + RBT_FOREACH_REVERSE_SAFE(ins, tree, &root, tmp) { + assert(RBT_REMOVE(tree, &root, ins) == ins); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done iterations in %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } +#endif + + if (test_target == NULL || + strcmp(test_target, "benchmarks") == 0 + ) { + TDEBUGF("doing 50%% insertions, 50%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, ITER / 2, ITER / 2, 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER / 2 + 1; i++) { + tmp = RBT_ROOT(tree, &root); + if (tmp == NULL) + errx(1, "RBT_ROOT error"); + if (RBT_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RBT_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing 20%% insertions, 80%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, ITER / 5, 4 * (ITER / 5), 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER / 5 + 1; i++) { + tmp = RBT_ROOT(tree, &root); + if (tmp == NULL) + errx(1, "RBT_ROOT error"); + if (RBT_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RBT_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing 10%% insertions, 90%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, ITER / 10, 9 * (ITER / 10), 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < ITER / 10 + 1; i++) { + tmp = RBT_ROOT(tree, &root); + if (tmp == NULL) + errx(1, "RBT_ROOT error"); + if (RBT_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RBT_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing 5%% insertions, 95%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, + ITER, 5 * (ITER / 100), + 95 * (ITER / 100), 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < 5 * (ITER / 100) + 1; i++) { + tmp = RBT_ROOT(tree, &root); + if (tmp == NULL) + errx(1, "RBT_ROOT error"); + if (RBT_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RBT_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing 2%% insertions, 98%% lookups"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + mix_operations(perm, ITER, nodes, ITER, + 2 * (ITER / 100), + 98 * (ITER / 100), 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done operations in: %lld.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + + TDEBUGF("doing root removals"); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + for (i = 0; i < 2 * (ITER / 100) + 1; i++) { + tmp = RBT_ROOT(tree, &root); + if (tmp == NULL) + errx(1, "RBT_ROOT error"); + if (RBT_REMOVE(tree, &root, tmp) != tmp) + errx(1, "RBT_REMOVE error"); + } + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + timespecsub(&end, &start, &diff); + TDEBUGF("done root removals in: %llu.%09llu s", + (unsigned long long)diff.tv_sec, (unsigned long long)diff.tv_nsec); + } + + free(nodes); + free(perm); + free(nums); + + return 0; +} + + +static int +compare(const struct node *a, const struct node *b) +{ + return a->key - b->key; +} + +#ifdef RBT_TEST_DIAGNOSTIC +static void +print_helper(const struct node *n, int indent) +{ + if (RBT_RIGHT(n, node_link)) + print_helper(RBT_RIGHT(n, node_link), indent + 4); + TDEBUGF("%*s key=%d :: size=%zu :: rank=%d :: rdiff %lu:%lu", + indent, "", n->key, n->size, RBT_RANK(tree, n), + _RBT_GET_RDIFF(n, _RBT_LDIR, node_link), + _RBT_GET_RDIFF(n, _RBT_RDIR, node_link)); + if (RBT_LEFT(n, node_link)) + print_helper(RBT_LEFT(n, node_link), indent + 4); +} + +static void +print_tree(const struct tree *t) +{ + if (RBT_ROOT(tree, t)) print_helper(RBT_ROOT(tree, t), 0); +} +#endif + +#ifdef DOAUGMENT +static int +tree_augment(struct node *elm) +{ + size_t newsize = 1, newheight = 0; + if ((RBT_LEFT(elm, node_link))) { + newsize += (RBT_LEFT(elm, node_link))->size; + newheight = MAX((RBT_LEFT(elm, node_link))->height, newheight); + } + if ((RBT_RIGHT(elm, node_link))) { + newsize += (RBT_RIGHT(elm, node_link))->size; + newheight = MAX((RBT_RIGHT(elm, node_link))->height, newheight); + } + newheight += 1; + if (elm->size != newsize || elm->height != newheight) { + elm->size = newsize; + elm->height = newheight; + return 1; + } + return 0; +} +#endif + + +void +mix_operations(int *perm, int psize, struct node *nodes, int nsize, + int insertions, int reads, int do_reads) +{ + int i, rank; + struct node *tmp, *ins; + struct node it; + assert(psize == nsize); + assert(insertions + reads <= psize); + + for(i = 0; i < insertions; i++) { + //TDEBUGF("iteration %d", i); + tmp = &(nodes[i]); + if (tmp == NULL) err(1, "malloc"); + tmp->size = 1; + tmp->height = 1; + tmp->key = perm[i]; + //TDEBUGF("inserting %d", tmp->key); + if (RBT_INSERT(tree, &root, tmp) != NULL) + errx(1, "RBT_INSERT failed"); + print_tree(&root); +#ifdef DOAUGMENT + //TDEBUGF("size = %zu", RBT_ROOT(tree, &root)->size); + assert(RBT_ROOT(tree, &root)->size == i + 1); +#endif + +#ifdef RBT_TEST_RANK + if (i % RANK_TEST_ITERATIONS == 0) { + rank = RBT_RANK(tree, RBT_ROOT(tree, &root)); + if (rank == -2) + errx(1, "rank error"); + } +#endif + } + tmp = &(nodes[insertions]); + tmp->key = ITER + 5; + tmp->size = 1; + tmp->height = 1; + RBT_INSERT(tree, &root, tmp); + if (do_reads) { + for (i = 0; i < insertions; i++) { + it.key = perm[i]; + ins = RBT_FIND(tree, &root, &it); + if ((ins == NULL) || ins->key != it.key) + errx(1, "RBT_FIND failed"); + } + for (i = insertions; i < insertions + reads; i++) { + it.key = perm[i]; + ins = RBT_NFIND(tree, &root, &it); + if (ins->key < it.key) + errx(1, "RBT_NFIND failed"); + } + } +}