Blender  V2.59
DLRB_tree.c
Go to the documentation of this file.
00001 /*
00002  * $Id: DLRB_tree.c 35246 2011-02-27 20:37:56Z jesterking $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
00021  * All rights reserved.
00022  *
00023  * Contributor(s): Joshua Leung (original author)
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include "MEM_guardedalloc.h"
00034 
00035 #include "BLI_blenlib.h"
00036 #include "BLI_dlrbTree.h"
00037 
00038 /* *********************************************** */
00039 /* Tree API */
00040 
00041 /* Create a new tree, and initialise as necessary */
00042 DLRBT_Tree *BLI_dlrbTree_new (void)
00043 {
00044         /* just allocate for now */
00045         return MEM_callocN(sizeof(DLRBT_Tree), "DLRBT_Tree");
00046 }
00047 
00048 /* Just zero out the pointers used */
00049 void BLI_dlrbTree_init (DLRBT_Tree *tree) 
00050 {
00051         if (tree == NULL)
00052                 return;
00053                 
00054         tree->first= tree->last= tree->root= NULL;
00055 }
00056 
00057 /* Helper for traversing tree and freeing sub-nodes */
00058 static void recursive_tree_free_nodes (DLRBT_Node *node)
00059 {
00060         /* sanity check */
00061         if (node == NULL)
00062                 return;
00063         
00064         /* free child nodes + subtrees */
00065         recursive_tree_free_nodes(node->left);
00066         recursive_tree_free_nodes(node->right);
00067         
00068         /* free self */
00069         MEM_freeN(node);
00070 }
00071 
00072 /* Free the given tree's data but not the tree itself */
00073 void BLI_dlrbTree_free (DLRBT_Tree *tree)
00074 {
00075         if (tree == NULL)
00076                 return;
00077         
00078         /* if the list-base stuff is set, just use that (and assume its set), 
00079          * otherwise, we'll need to traverse the tree...
00080          */
00081         if (tree->first) {
00082                 /* free list */
00083                 BLI_freelistN((ListBase *)tree);
00084         }
00085         else {
00086                 /* traverse tree, freeing sub-nodes */
00087                 recursive_tree_free_nodes(tree->root);
00088         }
00089         
00090         /* clear pointers */
00091         tree->first= tree->last= tree->root= NULL;
00092 }
00093 
00094 /* ------- */
00095 
00096 /* Helper function - used for traversing down the tree from the root to add nodes in order */
00097 static void linkedlist_sync_add_node (DLRBT_Tree *tree, DLRBT_Node *node)
00098 {
00099         /* sanity checks */
00100         if ((tree == NULL) || (node == NULL))
00101                 return;
00102         
00103         /* add left-node (and its subtree) */
00104         linkedlist_sync_add_node(tree, node->left);
00105         
00106         /* now add self
00107          *      - must remove detach from other links first
00108          *        (for now, only clear own pointers)
00109          */
00110         node->prev= node->next= NULL;
00111         BLI_addtail((ListBase *)tree, (Link *)node);
00112         
00113         /* finally, add right node (and its subtree) */
00114         linkedlist_sync_add_node(tree, node->right);
00115 }
00116 
00117 /* Make sure the tree's Double-Linked list representation is valid */
00118 void BLI_dlrbTree_linkedlist_sync (DLRBT_Tree *tree)
00119 {
00120         /* sanity checks */
00121         if (tree == NULL)
00122                 return;
00123                 
00124         /* clear list-base pointers so that the new list can be added properly */
00125         tree->first= tree->last= NULL;
00126         
00127         /* start adding items from the root */
00128         linkedlist_sync_add_node(tree, tree->root);
00129 }
00130 
00131 /* *********************************************** */
00132 /* Tree Search Utilities */
00133 
00134 /* Find the node which matches or is the closest to the requested node */
00135 DLRBT_Node *BLI_dlrbTree_search (DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
00136 {
00137         DLRBT_Node *node = (tree) ? tree->root : NULL;
00138         short found= 0;
00139         
00140         /* check that there is a comparator to use */
00141         // TODO: if no comparator is supplied, try using the one supplied with the tree...
00142         if (cmp_cb == NULL)
00143                 return NULL;
00144         
00145         /* iteratively perform this search */
00146         while (node && found==0) 
00147         {
00148                 /* check if traverse further or not 
00149                  * NOTE: it is assumed that the values will be unit values only
00150                  */
00151                 switch (cmp_cb(node, search_data)) {
00152                         case -1:        /* data less than node */
00153                                 if (node->left)
00154                                         node= node->left;
00155                                 else
00156                                         found= 1;
00157                                 break;
00158                         
00159                         case 1:         /* data greater than node */
00160                                 if (node->right)
00161                                         node= node->right;
00162                                 else
00163                                         found= 1;
00164                                 break;
00165                         
00166                         default:        /* data equals node */
00167                                 found= 1;
00168                                 break;
00169                 }
00170         }
00171         
00172         /* return the nearest matching node */
00173         return node;
00174 } 
00175 
00176 /* Find the node which exactly matches the required data */
00177 DLRBT_Node *BLI_dlrbTree_search_exact (DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
00178 {
00179         DLRBT_Node *node = (tree) ? tree->root : NULL;
00180         short found= 0;
00181         
00182         /* check that there is a comparator to use */
00183         // TODO: if no comparator is supplied, try using the one supplied with the tree...
00184         if (cmp_cb == NULL)
00185                 return NULL;
00186         
00187         /* iteratively perform this search */
00188         while (node && found==0) 
00189         {
00190                 /* check if traverse further or not 
00191                  * NOTE: it is assumed that the values will be unit values only
00192                  */
00193                 switch (cmp_cb(node, search_data)) {
00194                         case -1:        /* data less than node */
00195                                 if (node->left)
00196                                         node= node->left;
00197                                 else
00198                                         found= -1;
00199                                 break;
00200                         
00201                         case 1:         /* data greater than node */
00202                                 if (node->right)
00203                                         node= node->right;
00204                                 else
00205                                         found= -1;
00206                                 break;
00207                         
00208                         default:        /* data equals node */
00209                                 found= 1;
00210                                 break;
00211                 }
00212         }
00213         
00214         /* return the nearest matching node */
00215         return (found == 1) ? (node) : (NULL);
00216 }
00217 
00218 /* Find the node which occurs immediately before the best matching node */
00219 DLRBT_Node *BLI_dlrbTree_search_prev (DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
00220 {
00221         DLRBT_Node *node;
00222         
00223         /* check that there is a comparator to use */
00224         // TODO: if no comparator is supplied, try using the one supplied with the tree...
00225         if (cmp_cb == NULL)
00226                 return NULL;
00227         
00228         /* get the node which best matches this description */
00229         node= BLI_dlrbTree_search(tree, cmp_cb, search_data);
00230         
00231         if (node) {
00232                 /* if the item we're searching for is greater than the node found, we've found the match */
00233                 if (cmp_cb(node, search_data) > 0)
00234                         return node;
00235                 
00236                 /* return the previous node otherwise */
00237                 // NOTE: what happens if there is no previous node?
00238                 return node->prev;
00239         }
00240         
00241         /* nothing matching was found */
00242         return NULL;
00243 }
00244 
00245 /* Find the node which occurs immediately after the best matching node */
00246 DLRBT_Node *BLI_dlrbTree_search_next (DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
00247 {
00248         DLRBT_Node *node;
00249         
00250         /* check that there is a comparator to use */
00251         // TODO: if no comparator is supplied, try using the one supplied with the tree...
00252         if (cmp_cb == NULL)
00253                 return NULL;
00254         
00255         /* get the node which best matches this description */
00256         node= BLI_dlrbTree_search(tree, cmp_cb, search_data);
00257         
00258         if (node) {
00259                 /* if the item we're searching for is less than the node found, we've found the match */
00260                 if (cmp_cb(node, search_data) < 0)
00261                         return node;
00262                 
00263                 /* return the previous node otherwise */
00264                 // NOTE: what happens if there is no previous node?
00265                 return node->next;
00266         }
00267         
00268         /* nothing matching was found */
00269         return NULL;
00270 }
00271 
00272 
00273 /* Check whether there is a node matching the requested node */
00274 short BLI_dlrbTree_contains(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
00275 {
00276         /* check if an exact search throws up anything... */
00277         return (BLI_dlrbTree_search_exact(tree, cmp_cb, search_data) != NULL);
00278 }
00279 
00280 /* *********************************************** */
00281 /* Tree Relationships Utilities */
00282 
00283 /* get the 'grandparent' - the parent of the parent - of the given node */
00284 static DLRBT_Node *get_grandparent (DLRBT_Node *node)
00285 {
00286         if (node && node->parent)
00287                 return node->parent->parent;
00288         else
00289                 return NULL;
00290 }
00291 
00292 /* get the 'uncle' - the sibling of the parent - of the given node */
00293 static DLRBT_Node *get_uncle (DLRBT_Node *node)
00294 {
00295         DLRBT_Node *gpn= get_grandparent(node);
00296         
00297         /* return the child of the grandparent which isn't the node's parent */
00298         if (gpn) {
00299                 if (gpn->left == node->parent)
00300                         return gpn->right;
00301                 else
00302                         return gpn->left;
00303         }
00304         
00305         /* not found */
00306         return NULL;
00307 }
00308 
00309 /* *********************************************** */
00310 /* Tree Rotation Utilities */
00311 
00312 /* make right child of 'root' the new root */
00313 static void rotate_left (DLRBT_Tree *tree, DLRBT_Node *root)
00314 {
00315         DLRBT_Node **root_slot, *pivot;
00316         
00317         /* pivot is simply the root's right child, to become the root's parent */
00318         pivot= root->right;
00319         if (pivot == NULL)
00320                 return;
00321         
00322         if (root->parent) {
00323                 if (root == root->parent->left)
00324                         root_slot= &root->parent->left;
00325                 else
00326                         root_slot= &root->parent->right;
00327         }
00328         else
00329                 root_slot= ((DLRBT_Node**)&tree->root);//&((DLRBT_Node*)tree->root);
00330                 
00331         /* - pivot's left child becomes root's right child
00332          * - root now becomes pivot's left child  
00333          */
00334         root->right= pivot->left;       
00335         if (pivot->left) pivot->left->parent= root;
00336         
00337         pivot->left= root;
00338         pivot->parent= root->parent;
00339         root->parent= pivot;
00340         
00341         /* make the pivot the new root */
00342         if (root_slot)
00343                 *root_slot= pivot;
00344 }
00345 
00346 /* make the left child of the 'root' the new root */
00347 static void rotate_right (DLRBT_Tree *tree, DLRBT_Node *root)
00348 {
00349         DLRBT_Node **root_slot, *pivot;
00350         
00351         /* pivot is simply the root's left child, to become the root's parent */
00352         pivot= root->left;
00353         if (pivot == NULL)
00354                 return;
00355         
00356         if (root->parent) {
00357                 if (root == root->parent->left)
00358                         root_slot= &root->parent->left;
00359                 else
00360                         root_slot= &root->parent->right;
00361         }
00362         else
00363                 root_slot= ((DLRBT_Node**)&tree->root);//&((DLRBT_Node*)tree->root);
00364                 
00365         /* - pivot's right child becomes root's left child
00366          * - root now becomes pivot's right child  
00367          */
00368         root->left= pivot->right;       
00369         if (pivot->right) pivot->right->parent= root;
00370         
00371         pivot->right= root;
00372         pivot->parent= root->parent;
00373         root->parent= pivot;
00374         
00375         /* make the pivot the new root */
00376         if (root_slot)
00377                 *root_slot= pivot;
00378 }
00379 
00380 /* *********************************************** */
00381 /* Post-Insertion Balancing  */
00382 
00383 /* forward defines for insertion checks */
00384 static void insert_check_1(DLRBT_Tree *tree, DLRBT_Node *node);
00385 static void insert_check_2(DLRBT_Tree *tree, DLRBT_Node *node);
00386 static void insert_check_3(DLRBT_Tree *tree, DLRBT_Node *node);
00387 
00388 /* ----- */
00389 
00390 /* W. 1) Root must be black (so that the 2nd-generation can have a black parent) */
00391 static void insert_check_1 (DLRBT_Tree *tree, DLRBT_Node *node)
00392 {
00393         if (node) {
00394                 /* if this is the root, just ensure that it is black */
00395                 if (node->parent == NULL)
00396                         node->tree_col= DLRBT_BLACK;
00397                 else
00398                         insert_check_2(tree, node);
00399         }
00400 }
00401 
00402 /* W. 2+3) Parent of node must be black, otherwise recolor and flush */
00403 static void insert_check_2 (DLRBT_Tree *tree, DLRBT_Node *node)
00404 {
00405         /* if the parent is not black, we need to change that... */
00406         if (node && node->parent && node->parent->tree_col) {
00407                 DLRBT_Node *unc= get_uncle(node);
00408                 
00409                 /* if uncle and parent are both red, need to change them to black and make 
00410                  * the parent black in order to satisfy the criteria of each node having the
00411                  * same number of black nodes to its leaves
00412                  */
00413                 if (unc && unc->tree_col) {
00414                         DLRBT_Node *gp= get_grandparent(node);
00415                         
00416                         /* make the n-1 generation nodes black */
00417                         node->parent->tree_col= unc->tree_col= DLRBT_BLACK;
00418                         
00419                         /* - make the grandparent red, so that we maintain alternating red/black property 
00420                          *  (it must exist, so no need to check for NULL here),
00421                          * - as the grandparent may now cause inconsistencies with the rest of the tree, 
00422                          *   we must flush up the tree and perform checks/rebalancing/repainting, using the 
00423                          *      grandparent as the node of interest
00424                          */
00425                         gp->tree_col= DLRBT_RED;
00426                         insert_check_1(tree, gp);
00427                 }
00428                 else {
00429                         /* we've got an unbalanced branch going down the grandparent to the parent,
00430                          * so need to perform some rotations to re-balance the tree
00431                          */
00432                         insert_check_3(tree, node);
00433                 }
00434         }
00435 }
00436 
00437 /* W. 4+5) Perform rotation on sub-tree containing the 'new' node, then do any  */
00438 static void insert_check_3 (DLRBT_Tree *tree, DLRBT_Node *node)
00439 {
00440         DLRBT_Node *gp= get_grandparent(node);
00441         
00442         /* check that grandparent and node->parent exist (jut in case... really shouldn't happen on a good tree) */
00443         if (node && node->parent && gp) {
00444                 /* a left rotation will switch the roles of node and its parent, assuming that
00445                  * the parent is the left child of the grandparent... otherwise, rotation direction
00446                  * should be swapped
00447                  */
00448                 if ((node == node->parent->right) && (node->parent == gp->left)) {
00449                         rotate_left(tree, node);
00450                         node= node->left;
00451                 }
00452                 else if ((node == node->parent->left) && (node->parent == gp->right)) {
00453                         rotate_right(tree, node); 
00454                         node= node->right;
00455                 }
00456                 
00457                 /* fix old parent's color-tagging, and perform rotation on the old parent in the 
00458                  * opposite direction if needed for the current situation
00459                  * NOTE: in the code above, node pointer is changed to point to the old parent 
00460                  */
00461                 if (node) {
00462                         /* get 'new' grandparent (i.e. grandparent for old-parent (node)) */
00463                         gp= get_grandparent(node);
00464                         
00465                         /* modify the coloring of the grandparent and parent so that they still satisfy the constraints */
00466                         node->parent->tree_col= DLRBT_BLACK;
00467                         gp->tree_col= DLRBT_RED;
00468                         
00469                         /* if there are several nodes that all form a left chain, do a right rotation to correct this
00470                          * (or a rotation in the opposite direction if they all form a right chain)
00471                          */
00472                         if ((node == node->parent->left) && (node->parent == gp->left))
00473                                 rotate_right(tree, gp);
00474                         else //if ((node == node->parent->right) && (node->parent == gp->right))
00475                                 rotate_left(tree, gp);
00476                 }
00477         }
00478 }
00479 
00480 /* ----- */
00481 
00482 /* Balance the tree after the given element has been added to it 
00483  * (using custom code, in the Binary Tree way).
00484  */
00485 void BLI_dlrbTree_insert (DLRBT_Tree *tree, DLRBT_Node *node)
00486 {
00487         /* sanity checks */
00488         if ((tree == NULL) || (node == NULL))
00489                 return;
00490                 
00491         /* firstly, the node we just added should be red by default */
00492         node->tree_col= DLRBT_RED;
00493                 
00494         /* start from case 1, an trek through the tail-recursive insertion checks */
00495         insert_check_1(tree, node);
00496 }
00497 
00498 /* ----- */
00499 
00500 /* Add the given data to the tree, and return the node added */
00501 // NOTE: for duplicates, the update_cb is called (if available), and the existing node is returned
00502 DLRBT_Node *BLI_dlrbTree_add(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, 
00503                         DLRBT_NAlloc_FP new_cb, DLRBT_NUpdate_FP update_cb, void *data)
00504 {
00505         DLRBT_Node *parNode, *node=NULL;
00506         short new_node = 0;
00507         
00508         /* sanity checks */
00509         if (tree == NULL)
00510                 return NULL;
00511                 
00512         // TODO: if no comparator is supplied, try using the one supplied with the tree...
00513         if (cmp_cb == NULL)
00514                 return NULL;
00515         // TODO: if no allocator is supplied, try using the one supplied with the tree...
00516         if (new_cb == NULL)
00517                 return NULL;
00518         // TODO: if no updater is supplied, try using the one supplied with the tree...
00519                 
00520         /* try to find the nearest node to this one */
00521         parNode= BLI_dlrbTree_search(tree, cmp_cb, data);
00522         
00523         /* add new node to the BST in the 'standard way' as appropriate 
00524          * NOTE: we do not support duplicates in our tree...
00525          */
00526         if (parNode) {
00527                 /* check how this new node compares with the existing ones 
00528                  * NOTE: it is assumed that the values will be unit values only
00529                  */
00530                 switch (cmp_cb(parNode, data)) {
00531                         case -1:        /* add new node as left child */
00532                         {
00533                                 node= new_cb(data);
00534                                 new_node= 1;
00535                                 
00536                                 parNode->left= node;
00537                                 node->parent= parNode;
00538                         }
00539                                 break;
00540                         
00541                         case 1:         /* add new node as right child */
00542                         {
00543                                 node= new_cb(data);
00544                                 new_node= 1;
00545                                 
00546                                 parNode->right= node;
00547                                 node->parent= parNode;
00548                         }
00549                                 break;
00550                         
00551                         default:        /* update the duplicate node as appropriate */
00552                         {
00553                                 if (update_cb)
00554                                         update_cb(parNode, data);
00555                         }
00556                                 break;
00557                 }
00558         }
00559         else {
00560                 /* no nodes in the tree yet... add a new node as the root */
00561                 node= new_cb(data);
00562                 new_node= 1;
00563                 
00564                 tree->root= node;
00565         }
00566         
00567         /* if a new node was added, it should be tagged as red, and then balanced as appropriate */
00568         if (new_node) {
00569                 /* tag this new node as being 'red' */
00570                 node->tree_col= DLRBT_RED;
00571                 
00572                 /* perform BST balancing steps:
00573                  *      start from case 1, an trek through the tail-recursive insertion checks
00574                  */
00575                 insert_check_1(tree, node);
00576         }
00577         
00578         /* return the node added */
00579         return node;
00580 }
00581 
00582 /* *********************************************** */
00583 /* Remove */
00584 
00585 // TODO: this hasn't been coded yet, since this functionality was not needed by the author
00586 
00587 /* *********************************************** */