Back to home page

Enduro/X

 
 

    


0001 /*--------------------------------------------------------------------------
0002  *
0003  * test_rbtree.c
0004  *      Test correctness of red-black tree operations.
0005  *
0006  * Copyright (c) 2009-2023, PostgreSQL Global Development Group
0007  *
0008  * IDENTIFICATION
0009  *      src/test/modules/test_rbtree/test_rbtree.c
0010  *
0011  * -------------------------------------------------------------------------
0012  */
0013 
0014 #include <stdio.h>
0015 #include <stdlib.h>
0016 #include <cgreen/cgreen.h>
0017 #include <ndrstandard.h>
0018 #include <string.h>
0019 #include <unistd.h>
0020 #include "ndebug.h"
0021 #include <rbtree.h>
0022 
0023 int M_size;
0024 int M_delSize;
0025 
0026 /*
0027  * Our test trees store an integer key, and nothing else.
0028  */
0029 typedef struct ndrx_int_RBTree_Node
0030 {
0031     ndrx_rbt_node_t     rbtnode;
0032     int                 key;
0033 } ndrx_int_RBTree_Node_t;
0034 
0035 
0036 /*
0037  * Node comparator.  We don't worry about overflow in the subtraction,
0038  * since none of our test keys are negative.
0039  */
0040 static int ndrx_irbt_cmp(const ndrx_rbt_node_t *a, const ndrx_rbt_node_t *b, void *arg)
0041 {
0042     const ndrx_int_RBTree_Node_t *ea = (const ndrx_int_RBTree_Node_t *) a;
0043     const ndrx_int_RBTree_Node_t *eb = (const ndrx_int_RBTree_Node_t *) b;
0044 
0045     return ea->key - eb->key;
0046 }
0047 
0048 /*
0049  * Node combiner.  For testing purposes, just check that library doesn't
0050  * try to combine unequal keys.
0051  */
0052 static void ndrx_irbt_combine(ndrx_rbt_node_t *existing, const ndrx_rbt_node_t *newdata, void *arg)
0053 {
0054     const ndrx_int_RBTree_Node_t *eexist = (const ndrx_int_RBTree_Node_t *) existing;
0055     const ndrx_int_RBTree_Node_t *enew = (const ndrx_int_RBTree_Node_t *) newdata;
0056 
0057     assert_equal(eexist->key, enew->key);
0058     
0059     /* release new node */
0060     NDRX_FPFREE((void*)newdata);
0061        
0062 /*
0063     if (eexist->key != enew->key)
0064         NDRX_LOG(log_error, "red-black tree combines %d into %d",
0065              enew->key, eexist->key);
0066 */
0067     /* if combines OK, release the the new */
0068     
0069 }
0070 
0071 /* Node allocator */
0072 static ndrx_rbt_node_t *ndrx_irbt_alloc(void *arg)
0073 {
0074     return (ndrx_rbt_node_t *) NDRX_FPMALLOC(sizeof(ndrx_int_RBTree_Node_t), 0);
0075 }
0076 
0077 /* Node freer */
0078 static void ndrx_irbt_free(ndrx_rbt_node_t *node, void *arg)
0079 {
0080     NDRX_FPFREE(node);
0081 }
0082 
0083 /*
0084  * Create a red-black tree using our support functions
0085  */
0086 static ndrx_rbt_tree_t *ndrx_create_int_rbtree(void)
0087 {
0088     return ndrx_rbt_create(ndrx_irbt_cmp,
0089                       ndrx_irbt_combine,
0090                       ndrx_irbt_free,
0091                       NULL);
0092 }
0093 
0094 /*
0095  * Generate a random permutation of the integers 0..size-1
0096  */
0097 static int *ndrx_GetPermutation(int size)
0098 {
0099     int        *permutation;
0100     int         i;
0101 
0102     permutation = (int *) NDRX_FPMALLOC(size * sizeof(int), 0);
0103 
0104     permutation[0] = 0;
0105 
0106     /*
0107      * This is the "inside-out" variant of the Fisher-Yates shuffle algorithm.
0108      * Notionally, we append each new value to the array and then swap it with
0109      * a randomly-chosen array element (possibly including itself, else we
0110      * fail to generate permutations with the last integer last).  The swap
0111      * step can be optimized by combining it with the insertion.
0112      */
0113     for (i = 1; i < size; i++)
0114     {
0115         int j = ndrx_rand() % (i + 1);
0116 
0117         if (j < i)              /* avoid fetching undefined data if j=i */
0118             permutation[i] = permutation[j];
0119 
0120         permutation[j] = i;
0121     }
0122 
0123     return permutation;
0124 }
0125 
0126 /*
0127  * Populate an empty RBTree with "size" integers having the values
0128  * 0, step, 2*step, 3*step, ..., inserting them in random order
0129  */
0130 static void ndrx_rbt_populate(ndrx_rbt_tree_t *tree, int size, int step)
0131 {
0132     int                    *permutation = ndrx_GetPermutation(size);
0133     ndrx_int_RBTree_Node_t *node;
0134     int                    isNew = EXFALSE;
0135     int                    i;
0136     int                    isOk = EXTRUE;
0137 
0138     /* Insert values.  We don't expect any collisions. */
0139     for (i = 0; i < size; i++)
0140     {
0141         node=(ndrx_int_RBTree_Node_t *)ndrx_irbt_alloc(NULL);
0142         assert_not_equal(node, NULL);
0143         node->key = step * permutation[i];
0144         ndrx_rbt_insert(tree, (ndrx_rbt_node_t *) node, &isNew);
0145         if (!isNew){
0146             NDRX_LOG(log_error, "unexpected !isNew result from ndrx_rbt_insert");
0147             isOk = EXFALSE;
0148         }
0149     }
0150 
0151     assert_true(isOk);
0152 
0153     /*
0154      * Re-insert the first value to make sure collisions work right.  It's
0155      * probably not useful to test that case over again for all the values.
0156      */
0157     if (size > 0)
0158     {
0159         node=(ndrx_int_RBTree_Node_t *)ndrx_irbt_alloc(NULL);
0160         assert_not_equal(node, NULL);
0161 
0162         node->key = step * permutation[0];
0163         ndrx_rbt_insert(tree, (ndrx_rbt_node_t *) node, &isNew);
0164         assert_false_with_message(isNew, "unexpected isNew result from ndrx_rbt_insert");
0165     }
0166 
0167     NDRX_FPFREE(permutation);
0168 }
0169 
0170 /*  
0171  * Check the correctness of left-right traversal.
0172  * Left-right traversal is correct if all elements are
0173  * visited in increasing order.
0174  */
0175 Ensure(test_rbt_leftright)
0176 {
0177     ndrx_rbt_tree_t             *tree = ndrx_create_int_rbtree();
0178     ndrx_int_RBTree_Node_t      *node;
0179     ndrx_rbt_tree_iterator_t    iter;
0180     int lastKey = -1;
0181     int count = 0;
0182     int size = M_size;
0183 
0184     /* check iteration over empty tree */
0185     ndrx_rbt_begin_iterate(tree, LeftRightWalk, &iter);
0186 
0187     assert_true_with_message(ndrx_rbt_iterate(&iter) == NULL, "ndrx_rbt_iterate(&iter) != NULL");
0188 
0189     /* fill tree with consecutive natural numbers */
0190     ndrx_rbt_populate(tree, size, 1);
0191 
0192     /* iterate over the tree */
0193     ndrx_rbt_begin_iterate(tree, LeftRightWalk, &iter);
0194 
0195     while ((node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_iterate(&iter)) != NULL)
0196     {
0197         /* check that order is increasing */
0198         assert_true_with_message(node->key > lastKey, "left-right walk gives elements not in sorted order");
0199         lastKey = node->key;
0200         count++;
0201     }
0202 
0203     assert_true_with_message(lastKey == size - 1, "left-right walk did not reach end");
0204     assert_true_with_message(count == size, "left-right walk missed some elements");
0205 }
0206 
0207 /*
0208  * Check the correctness of right-left traversal.
0209  * Right-left traversal is correct if all elements are
0210  * visited in decreasing order.
0211  */
0212 Ensure (test_rbt_rightleft)
0213 {
0214     ndrx_rbt_tree_t             *tree = ndrx_create_int_rbtree();
0215     ndrx_int_RBTree_Node_t      *node;
0216     ndrx_rbt_tree_iterator_t    iter;
0217     int lastKey = M_size;
0218     int count = 0;
0219     int size = M_size;
0220 
0221     /* check iteration over empty tree */
0222     ndrx_rbt_begin_iterate(tree, RightLeftWalk, &iter);
0223     assert_true_with_message(ndrx_rbt_iterate(&iter) == NULL, "ndrx_rbt_iterate(&iter) != NULL");
0224 
0225     /* fill tree with consecutive natural numbers */
0226     ndrx_rbt_populate(tree, size, 1);
0227 
0228     /* iterate over the tree */
0229     ndrx_rbt_begin_iterate(tree, RightLeftWalk, &iter);
0230 
0231     while ((node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_iterate(&iter)) != NULL)
0232     {
0233         /* check that order is decreasing */
0234         assert_true_with_message(node->key < lastKey, "right-left walk gives elements not in sorted order");
0235 
0236         lastKey = node->key;
0237         count++;
0238     }
0239 
0240     assert_true_with_message(lastKey == 0, "right-left walk did not reach end");
0241     assert_true_with_message(count == size, "right-left walk missed some elements");
0242 }
0243 
0244 /*
0245  * Check the correctness of the rbt_find operation by searching for
0246  * both elements we inserted and elements we didn't.
0247  */
0248 Ensure(test_rbt_find)
0249 {
0250     ndrx_rbt_tree_t    *tree = ndrx_create_int_rbtree();
0251     int i;
0252     int size = M_size;
0253     int isOk = EXTRUE;
0254     /* Insert even integers from 0 to 2 * (size-1) */
0255     ndrx_rbt_populate(tree, size, 2);
0256 
0257     /* Check that all inserted elements can be found */
0258     for (i = 0; i < size; i++)
0259     {
0260         ndrx_int_RBTree_Node_t node;
0261         ndrx_int_RBTree_Node_t *resultNode;
0262 
0263         node.key = 2 * i;
0264         resultNode = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find(tree, (ndrx_rbt_node_t *) &node);
0265         assert_true_with_message(resultNode != NULL, "inserted element was not found");
0266         assert_true_with_message(resultNode->key == node.key, "find operation in rbtree gave wrong result");
0267     }
0268 
0269     /*
0270      * Check that not-inserted elements can not be found, being sure to try
0271      * values before the first and after the last element.
0272      */
0273     for (i = -1; i <= 2 * size; i += 2)
0274     {
0275         ndrx_int_RBTree_Node_t node;
0276         ndrx_int_RBTree_Node_t *resultNode;
0277 
0278         node.key = i;
0279         resultNode = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find(tree, (ndrx_rbt_node_t *) &node);
0280         if (resultNode != NULL)
0281         {
0282             NDRX_LOG(log_error, "not-inserted element was found");
0283             isOk = EXFALSE;
0284         }
0285     }
0286     assert_true_with_message(isOk, "not-inserted element was found");
0287 }
0288 
0289 /*
0290  * Check the correctness of the rbt_find_less() and rbt_find_great() functions
0291  * by searching for an equal key and iterating the lesser keys then the greater
0292  * keys.
0293  */
0294 Ensure(test_rbt_findltgt)
0295 {
0296     ndrx_rbt_tree_t *tree = ndrx_create_int_rbtree();
0297     int             size = M_size;
0298     int             isOk = EXTRUE;
0299 
0300     /*
0301      * Using the size as the random key to search wouldn't allow us to get at
0302      * least one greater match, so we do size - 1
0303      */
0304     int     randomKey = ndrx_rand() % (size - 1);
0305     int     keyDeleted;
0306     ndrx_int_RBTree_Node_t searchNode = {.key = randomKey};
0307     ndrx_int_RBTree_Node_t *lteNode;
0308     ndrx_int_RBTree_Node_t *gteNode;
0309     ndrx_int_RBTree_Node_t *node;
0310 
0311     /* Insert natural numbers */
0312     ndrx_rbt_populate(tree, size, 1);
0313 
0314     /*
0315      * Since the search key is included in the naturals of the tree, we're
0316      * sure to find an equal match
0317      */
0318     lteNode = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find_less(tree, (ndrx_rbt_node_t *) &searchNode, EXTRUE);
0319     gteNode = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find_great(tree, (ndrx_rbt_node_t *) &searchNode, EXTRUE);
0320 
0321     if (lteNode == NULL || lteNode->key != searchNode.key)
0322     {
0323         NDRX_LOG(log_error, "rbt_find_less() didn't find the equal key");
0324         assert_true(EXFALSE);
0325     }
0326 
0327     if (gteNode == NULL || gteNode->key != searchNode.key)
0328     {
0329         NDRX_LOG(log_error, "rbt_find_great() didn't find the equal key");
0330         assert_true(EXFALSE);
0331     }
0332 
0333     if (lteNode != gteNode)
0334     {
0335         NDRX_LOG(log_error, "rbt_find_less() and rbt_find_great() found different equal keys");
0336         assert_true(EXFALSE);
0337     }
0338 
0339     /* Find the rest of the naturals lesser than the search key */
0340     keyDeleted = EXFALSE;
0341     for (; searchNode.key > 0; searchNode.key--)
0342     {
0343         /*
0344          * Find the next key.  If the current key is deleted, we can pass
0345          * equal_match == true and still find the next one.
0346          */
0347         node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find_less(tree, (ndrx_rbt_node_t *) &searchNode,
0348                                                keyDeleted);
0349 
0350         /* ensure we find a lesser match */
0351         if (!node || !(node->key < searchNode.key))
0352         {
0353             NDRX_LOG(log_error, "rbt_find_less() didn't find a lesser key");
0354             isOk = EXFALSE;
0355         }
0356 
0357         /* randomly delete the found key or leave it */
0358         keyDeleted = ndrx_rand() % 2;
0359         if (keyDeleted)
0360         {
0361             ndrx_rbt_delete(tree, (ndrx_rbt_node_t *) node);
0362         }
0363     }
0364     assert_true(isOk); isOk = EXTRUE;
0365     /* Find the rest of the naturals greater than the search key */
0366     keyDeleted = EXFALSE;
0367     for (searchNode.key = randomKey; searchNode.key < size - 1; searchNode.key++)
0368     {
0369         /*
0370          * Find the next key.  If the current key is deleted, we can pass
0371          * equal_match == true and still find the next one.
0372          */
0373         node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find_great(tree, (ndrx_rbt_node_t *) &searchNode,
0374                                                 keyDeleted);
0375 
0376         /* ensure we find a greater match */
0377         if (!node || !(node->key > searchNode.key))
0378         {
0379             NDRX_LOG(log_error, "rbt_find_great() didn't find a greater key");
0380             isOk = EXFALSE;
0381         }
0382 
0383         /* randomly delete the found key or leave it */
0384         keyDeleted = ndrx_rand() % 2;
0385         if (keyDeleted)
0386             ndrx_rbt_delete(tree, (ndrx_rbt_node_t *) node);
0387     }
0388     assert_true(isOk); isOk = EXTRUE;
0389 
0390     /* Check out of bounds searches find nothing */
0391     searchNode.key = -1;
0392     node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find_less(tree, (ndrx_rbt_node_t *) &searchNode, EXTRUE);
0393     if (node != NULL)
0394     {
0395         NDRX_LOG(log_error, "rbt_find_less() found non-inserted element");
0396         assert_true(EXFALSE);
0397     }
0398     searchNode.key = 0;
0399     node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find_less(tree, (ndrx_rbt_node_t *) &searchNode, EXFALSE);
0400     if (node != NULL)
0401     {
0402         NDRX_LOG(log_error, "rbt_find_less() found non-inserted element");
0403         assert_true(EXFALSE);
0404     }
0405 
0406     searchNode.key = size;
0407     node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find_great(tree, (ndrx_rbt_node_t *) &searchNode, EXTRUE);
0408     if (node != NULL)
0409     {
0410         NDRX_LOG(log_error, "rbt_find_great() found non-inserted element");
0411         assert_true(EXFALSE);
0412     }
0413     searchNode.key = size - 1;
0414     node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find_great(tree, (ndrx_rbt_node_t *) &searchNode, EXFALSE);
0415     if (node != NULL)
0416     {
0417         NDRX_LOG(log_error, "rbt_find_great() found non-inserted element");
0418         assert_true(EXFALSE);
0419     }
0420 }
0421 
0422 /*
0423  * Check the correctness of the rbt_leftmost operation.
0424  * This operation should always return the smallest element of the tree.
0425  */
0426 Ensure(test_rbt_leftmost)
0427 {
0428     ndrx_rbt_tree_t         *tree = ndrx_create_int_rbtree();
0429     ndrx_int_RBTree_Node_t  *result;
0430     int                     size = M_size;
0431 
0432     /* Check that empty tree has no leftmost element */
0433     if (ndrx_rbt_leftmost(tree) != NULL)
0434     {
0435         NDRX_LOG(log_error, "leftmost node of empty tree is not NULL");
0436         assert_true(EXFALSE);
0437     }
0438 
0439     /* fill tree with consecutive natural numbers */
0440     ndrx_rbt_populate(tree, size, 1);
0441 
0442     /* Check that leftmost element is the smallest one */
0443     result = (ndrx_int_RBTree_Node_t *) ndrx_rbt_leftmost(tree);
0444     if (result == NULL || result->key != 0)
0445     {
0446         NDRX_LOG(log_error, "rbt_leftmost gave wrong result");
0447         assert_true(EXFALSE);
0448     }
0449 }
0450 
0451 /*
0452  * Check the correctness of the rbt_delete operation.
0453  */
0454 Ensure(test_rbt_delete)
0455 {
0456     ndrx_rbt_tree_t *tree = ndrx_create_int_rbtree();
0457     int             *deleteIds;
0458     int             i;
0459     int             size = M_size;
0460     int             delsize = M_delSize;
0461     int             isOk = EXTRUE;
0462 
0463     /* fill tree with consecutive natural numbers */
0464     ndrx_rbt_populate(tree, size, 1);
0465 
0466     deleteIds = (int *) NDRX_FPMALLOC(delsize * sizeof(int), 0);
0467 
0468     /* Choose unique and random ids to delete */
0469     for (i = 0; i < delsize; i++)
0470     {
0471         int k = ndrx_rand() % size;
0472 
0473         deleteIds[i] = k;
0474     }
0475 
0476     /* Delete elements */
0477     for (i = 0; i < delsize; i++)
0478     {
0479         ndrx_int_RBTree_Node_t find;
0480         ndrx_int_RBTree_Node_t *node;
0481 
0482         find.key = deleteIds[i];
0483         /* Locate the node to be deleted */
0484         node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find(tree, (ndrx_rbt_node_t *) &find);
0485         if (node == NULL || node->key != deleteIds[i])
0486         {
0487             /* Do not delete non-existing elements in the deleteIds array */
0488         }
0489         else
0490         {
0491             /* Delete it */
0492             ndrx_rbt_delete(tree, (ndrx_rbt_node_t *) node);
0493         }
0494     }
0495 
0496     /* Check that deleted elements are deleted */
0497     for (i = 0; i < delsize; i++)
0498     {
0499         ndrx_int_RBTree_Node_t find;
0500         ndrx_int_RBTree_Node_t *node;
0501 
0502         find.key = deleteIds[i];
0503         /* Locate the node to be deleted */
0504         node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find(tree, (ndrx_rbt_node_t *) &find);
0505         if (node != NULL)
0506         {
0507             NDRX_LOG(log_error, "deleted element still present in the rbtree");
0508             isOk = EXFALSE;
0509         }
0510     }
0511 
0512     assert_true(isOk); isOk = EXTRUE;
0513 
0514     /* Delete remaining elements, so as to exercise reducing tree to empty */
0515     for (i = 0; i < size; i++)
0516     {
0517         int j;
0518         ndrx_int_RBTree_Node_t find;
0519         ndrx_int_RBTree_Node_t *node;
0520 
0521         /* check if element should be deleted */
0522         for (j = 0; j < delsize; j++)
0523         {
0524             if (deleteIds[j] == i)
0525             {
0526                 break;
0527             }
0528         }
0529 
0530         find.key = i;
0531 
0532         /* Locate the node to be deleted */
0533         node = (ndrx_int_RBTree_Node_t *) ndrx_rbt_find(tree, (ndrx_rbt_node_t *) &find);
0534         if (node == NULL || node->key != i)
0535         {
0536             /* ok - element was not found during deleting */
0537         }
0538         else
0539         {
0540             /* Delete it */
0541             ndrx_rbt_delete(tree, (ndrx_rbt_node_t *) node);
0542         }
0543     }
0544 
0545     /* Tree should now be empty */
0546     if (ndrx_rbt_leftmost(tree) != NULL)
0547     {
0548         NDRX_LOG(log_error, "deleting all elements failed");
0549         assert_true(EXFALSE);
0550     }
0551 
0552     NDRX_FPFREE(deleteIds);
0553 }
0554 
0555 TestSuite *test_rbt_tree(void)
0556 {
0557     TestSuite *suite = create_test_suite();
0558 
0559 M_size    = 300;
0560 M_delSize = 200;
0561 
0562     add_test(suite, test_rbt_leftright);
0563     add_test(suite, test_rbt_rightleft);
0564     add_test(suite, test_rbt_find);
0565     add_test(suite, test_rbt_findltgt);
0566     add_test(suite, test_rbt_leftmost);
0567     add_test(suite, test_rbt_delete);
0568 
0569     return suite;
0570 }
0571 /* vim: set ts=4 sw=4 et smartindent: */