Blender V2.61 - r43446

DLRB_tree.c

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