This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenlib/intern/DLRB_tree.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

636 lines
17 KiB
C
Raw Normal View History

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
* All rights reserved.
*/
/** \file
* \ingroup bli
2011-02-27 20:37:56 +00:00
*/
#include "MEM_guardedalloc.h"
#include "BLI_dlrbTree.h"
#include "BLI_listbase.h"
/* *********************************************** */
/* Tree API */
/* Create a new tree, and initialize as necessary */
DLRBT_Tree *BLI_dlrbTree_new(void)
{
/* just allocate for now */
return MEM_callocN(sizeof(DLRBT_Tree), "DLRBT_Tree");
}
/* Just zero out the pointers used */
void BLI_dlrbTree_init(DLRBT_Tree *tree)
{
2019-03-27 13:16:10 +11:00
if (tree == NULL) {
return;
2019-03-27 13:16:10 +11:00
}
2018-06-17 16:32:54 +02:00
tree->first = tree->last = tree->root = NULL;
}
/* Helper for traversing tree and freeing sub-nodes */
static void recursive_tree_free_nodes(DLRBT_Node *node)
{
/* sanity check */
2019-03-27 13:16:10 +11:00
if (node == NULL) {
return;
2019-03-27 13:16:10 +11:00
}
2018-06-17 16:32:54 +02:00
/* free child nodes + subtrees */
recursive_tree_free_nodes(node->left);
recursive_tree_free_nodes(node->right);
2018-06-17 16:32:54 +02:00
/* free self */
MEM_freeN(node);
}
/* Free the given tree's data but not the tree itself */
void BLI_dlrbTree_free(DLRBT_Tree *tree)
{
2019-03-27 13:16:10 +11:00
if (tree == NULL) {
return;
2019-03-27 13:16:10 +11:00
}
2018-06-17 16:32:54 +02:00
/* if the list-base stuff is set, just use that (and assume its set),
* otherwise, we'll need to traverse the tree...
*/
if (tree->first) {
/* free list */
BLI_freelistN((ListBase *)tree);
}
else {
/* traverse tree, freeing sub-nodes */
recursive_tree_free_nodes(tree->root);
}
/* clear pointers */
tree->first = tree->last = tree->root = NULL;
}
/* ------- */
/* Helper function - used for traversing down the tree from the root to add nodes in order */
static void linkedlist_sync_add_node(DLRBT_Tree *tree, DLRBT_Node *node)
{
/* sanity checks */
2019-03-27 13:16:10 +11:00
if ((tree == NULL) || (node == NULL)) {
return;
2019-03-27 13:16:10 +11:00
}
/* add left-node (and its subtree) */
linkedlist_sync_add_node(tree, node->left);
/* now add self
2018-11-14 12:53:15 +11:00
* - must remove detach from other links first
* (for now, only clear own pointers)
*/
node->prev = node->next = NULL;
BLI_addtail((ListBase *)tree, (Link *)node);
/* finally, add right node (and its subtree) */
linkedlist_sync_add_node(tree, node->right);
}
/* Make sure the tree's Double-Linked list representation is valid */
void BLI_dlrbTree_linkedlist_sync(DLRBT_Tree *tree)
{
/* sanity checks */
2019-03-27 13:16:10 +11:00
if (tree == NULL) {
return;
2019-03-27 13:16:10 +11:00
}
2018-06-17 16:32:54 +02:00
/* clear list-base pointers so that the new list can be added properly */
tree->first = tree->last = NULL;
2018-06-17 16:32:54 +02:00
/* start adding items from the root */
linkedlist_sync_add_node(tree, tree->root);
}
/* *********************************************** */
/* Tree Search Utilities */
/* Find the node which matches or is the closest to the requested node */
DLRBT_Node *BLI_dlrbTree_search(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
{
DLRBT_Node *node = (tree) ? tree->root : NULL;
short found = 0;
/* check that there is a comparator to use */
/* TODO: if no comparator is supplied, try using the one supplied with the tree... */
2019-03-27 13:16:10 +11:00
if (cmp_cb == NULL) {
return NULL;
2019-03-27 13:16:10 +11:00
}
/* iteratively perform this search */
while (node && found == 0) {
2018-06-17 16:32:54 +02:00
/* check if traverse further or not
* NOTE: it is assumed that the values will be unit values only
*/
switch (cmp_cb(node, search_data)) {
case -1: /* data less than node */
2019-03-27 13:16:10 +11:00
if (node->left) {
node = node->left;
2019-03-27 13:16:10 +11:00
}
else {
found = 1;
2019-03-27 13:16:10 +11:00
}
break;
case 1: /* data greater than node */
2019-03-27 13:16:10 +11:00
if (node->right) {
node = node->right;
2019-03-27 13:16:10 +11:00
}
else {
found = 1;
2019-03-27 13:16:10 +11:00
}
break;
default: /* data equals node */
found = 1;
break;
}
}
/* return the nearest matching node */
return node;
2018-06-17 16:32:54 +02:00
}
/* Find the node which exactly matches the required data */
DLRBT_Node *BLI_dlrbTree_search_exact(DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data)
{
DLRBT_Node *node = (tree) ? tree->root : NULL;
short found = 0;
/* check that there is a comparator to use */
/* TODO: if no comparator is supplied, try using the one supplied with the tree... */
2019-03-27 13:16:10 +11:00
if (cmp_cb == NULL) {
return NULL;
2019-03-27 13:16:10 +11:00
}
/* iteratively perform this search */
while (node && found == 0) {
2018-06-17 16:32:54 +02:00
/* check if traverse further or not
* NOTE: it is assumed that the values will be unit values only
*/
switch (cmp_cb(node, search_data)) {
case -1: /* data less than node */
2019-03-27 13:16:10 +11:00
if (node->left) {
node = node->left;
2019-03-27 13:16:10 +11:00
}
else {
found = -1;
2019-03-27 13:16:10 +11:00
}
break;
case 1: /* data greater than node */
2019-03-27 13:16:10 +11:00
if (node->right) {
node = node->right;
2019-03-27 13:16:10 +11:00
}
else {
found = -1;
2019-03-27 13:16:10 +11:00
}
break;
default: /* data equals node */
found = 1;
break;
}
}
/* return the exactly matching node */
return (found == 1) ? (node) : (NULL);
}
/* Find the node which occurs immediately before the best matching node */
DLRBT_Node *BLI_dlrbTree_search_prev(DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data)
{
DLRBT_Node *node;
/* check that there is a comparator to use */
/* TODO: if no comparator is supplied, try using the one supplied with the tree... */
2019-03-27 13:16:10 +11:00
if (cmp_cb == NULL) {
return NULL;
2019-03-27 13:16:10 +11:00
}
/* get the node which best matches this description */
node = BLI_dlrbTree_search(tree, cmp_cb, search_data);
if (node) {
/* if the item we're searching for is greater than the node found, we've found the match */
2019-03-27 13:16:10 +11:00
if (cmp_cb(node, search_data) > 0) {
return node;
2019-03-27 13:16:10 +11:00
}
/* return the previous node otherwise */
2012-07-07 22:51:57 +00:00
/* NOTE: what happens if there is no previous node? */
return node->prev;
}
/* nothing matching was found */
return NULL;
}
/* Find the node which occurs immediately after the best matching node */
DLRBT_Node *BLI_dlrbTree_search_next(DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
void *search_data)
{
DLRBT_Node *node;
/* check that there is a comparator to use */
/* TODO: if no comparator is supplied, try using the one supplied with the tree... */
2019-03-27 13:16:10 +11:00
if (cmp_cb == NULL) {
return NULL;
2019-03-27 13:16:10 +11:00
}
/* get the node which best matches this description */
node = BLI_dlrbTree_search(tree, cmp_cb, search_data);
if (node) {
/* if the item we're searching for is less than the node found, we've found the match */
2019-03-27 13:16:10 +11:00
if (cmp_cb(node, search_data) < 0) {
return node;
2019-03-27 13:16:10 +11:00
}
/* return the previous node otherwise */
2012-07-07 22:51:57 +00:00
/* NOTE: what happens if there is no previous node? */
return node->next;
}
/* nothing matching was found */
return NULL;
}
/* Check whether there is a node matching the requested node */
short BLI_dlrbTree_contains(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data)
{
/* check if an exact search throws up anything... */
return (BLI_dlrbTree_search_exact(tree, cmp_cb, search_data) != NULL);
}
/* *********************************************** */
/* Tree Relationships Utilities */
/* get the 'grandparent' - the parent of the parent - of the given node */
static DLRBT_Node *get_grandparent(DLRBT_Node *node)
{
2019-03-27 13:16:10 +11:00
if (node && node->parent) {
return node->parent->parent;
2019-03-27 13:16:10 +11:00
}
return NULL;
}
/* get the sibling node (e.g. if node is left child of parent, return right child of parent) */
static DLRBT_Node *get_sibling(DLRBT_Node *node)
{
if (node && node->parent) {
2019-03-27 13:16:10 +11:00
if (node == node->parent->left) {
return node->parent->right;
2019-03-27 13:16:10 +11:00
}
return node->parent->left;
}
/* sibling not found */
return NULL;
}
/* get the 'uncle' - the sibling of the parent - of the given node */
static DLRBT_Node *get_uncle(DLRBT_Node *node)
{
2019-03-27 13:16:10 +11:00
if (node) {
/* return the child of the grandparent which isn't the node's parent */
return get_sibling(node->parent);
2019-03-27 13:16:10 +11:00
}
2018-06-17 16:32:54 +02:00
/* uncle not found */
return NULL;
}
/* *********************************************** */
/* Tree Rotation Utilities */
/* make right child of 'root' the new root */
static void rotate_left(DLRBT_Tree *tree, DLRBT_Node *root)
{
DLRBT_Node **root_slot, *pivot;
/* pivot is simply the root's right child, to become the root's parent */
pivot = root->right;
2019-03-27 13:16:10 +11:00
if (pivot == NULL) {
return;
2019-03-27 13:16:10 +11:00
}
if (root->parent) {
2019-03-27 13:16:10 +11:00
if (root == root->parent->left) {
root_slot = &root->parent->left;
2019-03-27 13:16:10 +11:00
}
else {
root_slot = &root->parent->right;
2019-03-27 13:16:10 +11:00
}
}
2019-03-27 13:16:10 +11:00
else {
2012-07-29 00:20:28 +00:00
root_slot = ((DLRBT_Node **)&tree->root); /* &((DLRBT_Node *)tree->root); */
2019-03-27 13:16:10 +11:00
}
/* - pivot's left child becomes root's right child
2018-06-17 16:32:54 +02:00
* - root now becomes pivot's left child
*/
root->right = pivot->left;
2019-03-27 13:16:10 +11:00
if (pivot->left) {
pivot->left->parent = root;
}
pivot->left = root;
pivot->parent = root->parent;
root->parent = pivot;
/* make the pivot the new root */
2019-03-27 13:16:10 +11:00
if (root_slot) {
*root_slot = pivot;
2019-03-27 13:16:10 +11:00
}
}
/* make the left child of the 'root' the new root */
static void rotate_right(DLRBT_Tree *tree, DLRBT_Node *root)
{
DLRBT_Node **root_slot, *pivot;
/* pivot is simply the root's left child, to become the root's parent */
pivot = root->left;
2019-03-27 13:16:10 +11:00
if (pivot == NULL) {
return;
2019-03-27 13:16:10 +11:00
}
if (root->parent) {
2019-03-27 13:16:10 +11:00
if (root == root->parent->left) {
root_slot = &root->parent->left;
2019-03-27 13:16:10 +11:00
}
else {
root_slot = &root->parent->right;
2019-03-27 13:16:10 +11:00
}
}
2019-03-27 13:16:10 +11:00
else {
2012-09-30 06:12:47 +00:00
root_slot = ((DLRBT_Node **)&tree->root); /* &((DLRBT_Node *)tree->root); */
2019-03-27 13:16:10 +11:00
}
/* - pivot's right child becomes root's left child
2018-06-17 16:32:54 +02:00
* - root now becomes pivot's right child
*/
root->left = pivot->right;
2019-03-27 13:16:10 +11:00
if (pivot->right) {
pivot->right->parent = root;
}
pivot->right = root;
pivot->parent = root->parent;
root->parent = pivot;
/* make the pivot the new root */
2019-03-27 13:16:10 +11:00
if (root_slot) {
*root_slot = pivot;
2019-03-27 13:16:10 +11:00
}
}
/* *********************************************** */
/* Post-Insertion Balancing */
/* forward defines for insertion checks */
static void insert_check_1(DLRBT_Tree *tree, DLRBT_Node *node);
static void insert_check_2(DLRBT_Tree *tree, DLRBT_Node *node);
static void insert_check_3(DLRBT_Tree *tree, DLRBT_Node *node);
/* ----- */
/* W. 1) Root must be black (so that the 2nd-generation can have a black parent) */
static void insert_check_1(DLRBT_Tree *tree, DLRBT_Node *node)
{
if (node) {
/* if this is the root, just ensure that it is black */
2019-03-27 13:16:10 +11:00
if (node->parent == NULL) {
node->tree_col = DLRBT_BLACK;
2019-03-27 13:16:10 +11:00
}
else {
insert_check_2(tree, node);
2019-03-27 13:16:10 +11:00
}
}
}
/* W. 2+3) Parent of node must be black, otherwise recolor and flush */
static void insert_check_2(DLRBT_Tree *tree, DLRBT_Node *node)
{
/* if the parent is not black, we need to change that... */
if (node && node->parent && node->parent->tree_col) {
DLRBT_Node *unc = get_uncle(node);
2018-06-17 16:32:54 +02:00
/* if uncle and parent are both red, need to change them to black and make
* the parent black in order to satisfy the criteria of each node having the
* same number of black nodes to its leaves
*/
if (unc && unc->tree_col) {
DLRBT_Node *gp = get_grandparent(node);
/* make the n-1 generation nodes black */
node->parent->tree_col = unc->tree_col = DLRBT_BLACK;
2018-06-17 16:32:54 +02:00
/* - make the grandparent red, so that we maintain alternating red/black property
* (it must exist, so no need to check for NULL here),
2018-06-17 16:32:54 +02:00
* - as the grandparent may now cause inconsistencies with the rest of the tree,
2012-07-16 23:23:33 +00:00
* we must flush up the tree and perform checks/re-balancing/re-painting, using the
* grandparent as the node of interest
*/
gp->tree_col = DLRBT_RED;
insert_check_1(tree, gp);
}
else {
/* we've got an unbalanced branch going down the grandparent to the parent,
* so need to perform some rotations to re-balance the tree
*/
insert_check_3(tree, node);
}
}
}
/* W. 4+5) Perform rotation on sub-tree containing the 'new' node, then do any */
static void insert_check_3(DLRBT_Tree *tree, DLRBT_Node *node)
{
DLRBT_Node *gp = get_grandparent(node);
/* check that grandparent and node->parent exist
* (jut in case... really shouldn't happen on a good tree) */
if (node && node->parent && gp) {
/* a left rotation will switch the roles of node and its parent, assuming that
* the parent is the left child of the grandparent... otherwise, rotation direction
* should be swapped
*/
if ((node == node->parent->right) && (node->parent == gp->left)) {
rotate_left(tree, node);
node = node->left;
}
else if ((node == node->parent->left) && (node->parent == gp->right)) {
2018-06-17 16:32:54 +02:00
rotate_right(tree, node);
node = node->right;
}
2018-06-17 16:32:54 +02:00
/* fix old parent's color-tagging, and perform rotation on the old parent in the
* opposite direction if needed for the current situation
2018-06-17 16:32:54 +02:00
* NOTE: in the code above, node pointer is changed to point to the old parent
*/
if (node) {
/* get 'new' grandparent (i.e. grandparent for old-parent (node)) */
gp = get_grandparent(node);
/* modify the coloring of the grandparent and parent
* so that they still satisfy the constraints */
node->parent->tree_col = DLRBT_BLACK;
gp->tree_col = DLRBT_RED;
/* if there are several nodes that all form a left chain, do a right rotation to correct
* this (or a rotation in the opposite direction if they all form a right chain) */
2019-03-27 13:16:10 +11:00
if ((node == node->parent->left) && (node->parent == gp->left)) {
rotate_right(tree, gp);
2019-03-27 13:16:10 +11:00
}
else { // if ((node == node->parent->right) && (node->parent == gp->right))
rotate_left(tree, gp);
2019-03-27 13:16:10 +11:00
}
}
}
}
/* ----- */
2018-06-17 16:32:54 +02:00
/* Balance the tree after the given element has been added to it
* (using custom code, in the Binary Tree way).
*/
void BLI_dlrbTree_insert(DLRBT_Tree *tree, DLRBT_Node *node)
{
/* sanity checks */
2019-03-27 13:16:10 +11:00
if ((tree == NULL) || (node == NULL)) {
return;
2019-03-27 13:16:10 +11:00
}
2018-06-17 16:32:54 +02:00
/* firstly, the node we just added should be red by default */
node->tree_col = DLRBT_RED;
2018-06-17 16:32:54 +02:00
/* start from case 1, an trek through the tail-recursive insertion checks */
insert_check_1(tree, node);
}
/* ----- */
/* Add the given data to the tree, and return the node added */
/* NOTE: for duplicates, the update_cb is called (if available),
* and the existing node is returned */
2018-06-17 16:32:54 +02:00
DLRBT_Node *BLI_dlrbTree_add(DLRBT_Tree *tree,
DLRBT_Comparator_FP cmp_cb,
DLRBT_NAlloc_FP new_cb,
DLRBT_NUpdate_FP update_cb,
void *data)
{
DLRBT_Node *parNode, *node = NULL;
short new_node = 0;
/* sanity checks */
2019-03-27 13:16:10 +11:00
if (tree == NULL) {
return NULL;
2019-03-27 13:16:10 +11:00
}
/* TODO: if no comparator is supplied, try using the one supplied with the tree... */
2019-03-27 13:16:10 +11:00
if (cmp_cb == NULL) {
return NULL;
2019-03-27 13:16:10 +11:00
}
/* TODO: if no allocator is supplied, try using the one supplied with the tree... */
2019-03-27 13:16:10 +11:00
if (new_cb == NULL) {
return NULL;
2019-03-27 13:16:10 +11:00
}
/* TODO: if no updater is supplied, try using the one supplied with the tree... */
/* try to find the nearest node to this one */
parNode = BLI_dlrbTree_search(tree, cmp_cb, data);
2018-06-17 16:32:54 +02:00
/* add new node to the BST in the 'standard way' as appropriate
* NOTE: we do not support duplicates in our tree...
*/
if (parNode) {
2018-06-17 16:32:54 +02:00
/* check how this new node compares with the existing ones
* NOTE: it is assumed that the values will be unit values only
*/
switch (cmp_cb(parNode, data)) {
case -1: /* add new node as left child */
{
node = new_cb(data);
new_node = 1;
parNode->left = node;
node->parent = parNode;
break;
}
case 1: /* add new node as right child */
{
node = new_cb(data);
new_node = 1;
parNode->right = node;
node->parent = parNode;
break;
}
default: /* update the duplicate node as appropriate */
{
2019-03-27 13:16:10 +11:00
if (update_cb) {
update_cb(parNode, data);
2019-03-27 13:16:10 +11:00
}
break;
}
}
}
else {
/* no nodes in the tree yet... add a new node as the root */
node = new_cb(data);
new_node = 1;
tree->root = node;
}
/* if a new node was added, it should be tagged as red, and then balanced as appropriate */
if (new_node) {
/* tag this new node as being 'red' */
node->tree_col = DLRBT_RED;
/* perform BST balancing steps:
2018-09-02 18:51:31 +10:00
* start from case 1, an trek through the tail-recursive insertion checks
*/
insert_check_1(tree, node);
}
/* return the node added */
return node;
}
/* *********************************************** */
/* Remove */
/* TODO: this hasn't been coded yet, since this functionality was not needed by the author */
/* *********************************************** */