/******************************************************************************* * Copyright (c) 2009, 2020 IBM Corp. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * https://www.eclipse.org/legal/epl-2.0/ * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Ian Craggs - initial implementation and documentation *******************************************************************************/ /** @file * \brief functions which apply to tree structures. * * These trees can hold data of any sort, pointed to by the content pointer of the * Node structure. * */ #define TREE_C /* so that malloc/free/realloc aren't redefined by Heap.h */ #include "Tree.h" #include #include #include #include "Heap.h" int isRed(Node* aNode); int isBlack(Node* aNode); /*int TreeWalk(Node* curnode, int depth);*/ /*int TreeMaxDepth(Tree *aTree);*/ void TreeRotate(Tree* aTree, Node* curnode, int direction, int index); Node* TreeBAASub(Tree* aTree, Node* curnode, int which, int index); void TreeBalanceAfterAdd(Tree* aTree, Node* curnode, int index); void* TreeAddByIndex(Tree* aTree, void* content, size_t size, int index); Node* TreeFindIndex1(Tree* aTree, void* key, int index, int value); Node* TreeFindContentIndex(Tree* aTree, void* key, int index); Node* TreeMinimum(Node* curnode); Node* TreeSuccessor(Node* curnode); Node* TreeNextElementIndex(Tree* aTree, Node* curnode, int index); Node* TreeBARSub(Tree* aTree, Node* curnode, int which, int index); void TreeBalanceAfterRemove(Tree* aTree, Node* curnode, int index); void* TreeRemoveIndex(Tree* aTree, void* content, int index); void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int)) { memset(aTree, '\0', sizeof(Tree)); aTree->heap_tracking = 1; aTree->index[0].compare = compare; aTree->indexes = 1; } /** * Allocates and initializes a new tree structure. * @return a pointer to the new tree structure */ Tree* TreeInitialize(int(*compare)(void*, void*, int)) { #if defined(UNIT_TESTS) || defined(NO_HEAP_TRACKING) Tree* newt = malloc(sizeof(Tree)); #else Tree* newt = mymalloc(__FILE__, __LINE__, sizeof(Tree)); #endif if (newt) TreeInitializeNoMalloc(newt, compare); return newt; } void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int)) { aTree->index[aTree->indexes].compare = compare; ++(aTree->indexes); } void TreeFree(Tree* aTree) { #if defined(UNIT_TESTS) || defined(NO_HEAP_TRACKING) free(aTree); #else (aTree->heap_tracking) ? myfree(__FILE__, __LINE__, aTree) : free(aTree); #endif } #define LEFT 0 #define RIGHT 1 #if !defined(max) #define max(a, b) (a > b) ? a : b; #endif int isRed(Node* aNode) { return (aNode != NULL) && (aNode->red); } int isBlack(Node* aNode) { return (aNode == NULL) || (aNode->red == 0); } #if 0 int TreeWalk(Node* curnode, int depth) { if (curnode) { int left = TreeWalk(curnode->child[LEFT], depth+1); int right = TreeWalk(curnode->child[RIGHT], depth+1); depth = max(left, right); if (curnode->red) { /*if (isRed(curnode->child[LEFT]) || isRed(curnode->child[RIGHT])) { printf("red/black tree violation %p\n", curnode->content); exit(-99); }*/; } } return depth; } int TreeMaxDepth(Tree *aTree) { int rc = TreeWalk(aTree->index[0].root, 0); /*if (aTree->root->red) { printf("root node should not be red %p\n", aTree->root->content); exit(-99); }*/ return rc; } #endif void TreeRotate(Tree* aTree, Node* curnode, int direction, int index) { Node* other = curnode->child[!direction]; curnode->child[!direction] = other->child[direction]; if (other->child[direction] != NULL) other->child[direction]->parent = curnode; other->parent = curnode->parent; if (curnode->parent == NULL) aTree->index[index].root = other; else if (curnode == curnode->parent->child[direction]) curnode->parent->child[direction] = other; else curnode->parent->child[!direction] = other; other->child[direction] = curnode; curnode->parent = other; } Node* TreeBAASub(Tree* aTree, Node* curnode, int which, int index) { Node* uncle = curnode->parent->parent->child[which]; if (isRed(uncle)) { curnode->parent->red = uncle->red = 0; curnode = curnode->parent->parent; curnode->red = 1; } else { if (curnode == curnode->parent->child[which]) { curnode = curnode->parent; TreeRotate(aTree, curnode, !which, index); } curnode->parent->red = 0; curnode->parent->parent->red = 1; TreeRotate(aTree, curnode->parent->parent, which, index); } return curnode; } void TreeBalanceAfterAdd(Tree* aTree, Node* curnode, int index) { while (curnode && isRed(curnode->parent) && curnode->parent->parent) { if (curnode->parent == curnode->parent->parent->child[LEFT]) curnode = TreeBAASub(aTree, curnode, RIGHT, index); else curnode = TreeBAASub(aTree, curnode, LEFT, index); } aTree->index[index].root->red = 0; } /** * Add an item to a tree * @param aTree the list to which the item is to be added * @param content the list item content itself * @param size the size of the element */ void* TreeAddByIndex(Tree* aTree, void* content, size_t size, int index) { Node* curparent = NULL; Node* curnode = aTree->index[index].root; Node* newel = NULL; int left = 0; int result = 1; void* rc = NULL; while (curnode) { result = aTree->index[index].compare(curnode->content, content, 1); left = (result > 0); if (result == 0) break; else { curparent = curnode; curnode = curnode->child[left]; } } if (result == 0) { if (aTree->allow_duplicates) goto exit; /* exit(-99); */ else { newel = curnode; if (index == 0) aTree->size += (size - curnode->size); } } else { #if defined(UNIT_TESTS) || defined(NO_HEAP_TRACKING) newel = malloc(sizeof(Node)); #else newel = (aTree->heap_tracking) ? mymalloc(__FILE__, __LINE__, sizeof(Node)) : malloc(sizeof(Node)); #endif if (newel == NULL) goto exit; memset(newel, '\0', sizeof(Node)); if (curparent) curparent->child[left] = newel; else aTree->index[index].root = newel; newel->parent = curparent; newel->red = 1; if (index == 0) { ++(aTree->count); aTree->size += size; } } newel->content = content; newel->size = size; rc = newel->content; TreeBalanceAfterAdd(aTree, newel, index); exit: return rc; } void* TreeAdd(Tree* aTree, void* content, size_t size) { void* rc = NULL; int i; for (i = 0; i < aTree->indexes; ++i) rc = TreeAddByIndex(aTree, content, size, i); return rc; } Node* TreeFindIndex1(Tree* aTree, void* key, int index, int value) { int result = 0; Node* curnode = aTree->index[index].root; while (curnode) { result = aTree->index[index].compare(curnode->content, key, value); if (result == 0) break; else curnode = curnode->child[result > 0]; } return curnode; } Node* TreeFindIndex(Tree* aTree, void* key, int index) { return TreeFindIndex1(aTree, key, index, 0); } Node* TreeFindContentIndex(Tree* aTree, void* key, int index) { return TreeFindIndex1(aTree, key, index, 1); } Node* TreeFind(Tree* aTree, void* key) { return TreeFindIndex(aTree, key, 0); } Node* TreeMinimum(Node* curnode) { if (curnode) while (curnode->child[LEFT]) curnode = curnode->child[LEFT]; return curnode; } Node* TreeSuccessor(Node* curnode) { if (curnode->child[RIGHT]) curnode = TreeMinimum(curnode->child[RIGHT]); else { Node* curparent = curnode->parent; while (curparent && curnode == curparent->child[RIGHT]) { curnode = curparent; curparent = curparent->parent; } curnode = curparent; } return curnode; } Node* TreeNextElementIndex(Tree* aTree, Node* curnode, int index) { if (curnode == NULL) curnode = TreeMinimum(aTree->index[index].root); else curnode = TreeSuccessor(curnode); return curnode; } Node* TreeNextElement(Tree* aTree, Node* curnode) { return TreeNextElementIndex(aTree, curnode, 0); } Node* TreeBARSub(Tree* aTree, Node* curnode, int which, int index) { Node* sibling = curnode->parent->child[which]; if (isRed(sibling)) { sibling->red = 0; curnode->parent->red = 1; TreeRotate(aTree, curnode->parent, !which, index); sibling = curnode->parent->child[which]; } if (!sibling) curnode = curnode->parent; else if (isBlack(sibling->child[!which]) && isBlack(sibling->child[which])) { sibling->red = 1; curnode = curnode->parent; } else { if (isBlack(sibling->child[which])) { sibling->child[!which]->red = 0; sibling->red = 1; TreeRotate(aTree, sibling, which, index); sibling = curnode->parent->child[which]; } sibling->red = curnode->parent->red; curnode->parent->red = 0; sibling->child[which]->red = 0; TreeRotate(aTree, curnode->parent, !which, index); curnode = aTree->index[index].root; } return curnode; } void TreeBalanceAfterRemove(Tree* aTree, Node* curnode, int index) { while (curnode != aTree->index[index].root && isBlack(curnode)) { /* curnode->content == NULL must equal curnode == NULL */ if (((curnode->content) ? curnode : NULL) == curnode->parent->child[LEFT]) curnode = TreeBARSub(aTree, curnode, RIGHT, index); else curnode = TreeBARSub(aTree, curnode, LEFT, index); } curnode->red = 0; } /** * Remove an item from a tree * @param aTree the list to which the item is to be added * @param curnode the list item content itself */ void* TreeRemoveNodeIndex(Tree* aTree, Node* curnode, int index) { Node* redundant = curnode; Node* curchild = NULL; size_t size = curnode->size; void* content = curnode->content; /* if the node to remove has 0 or 1 children, it can be removed without involving another node */ if (curnode->child[LEFT] && curnode->child[RIGHT]) /* 2 children */ redundant = TreeSuccessor(curnode); /* now redundant must have at most one child */ curchild = redundant->child[(redundant->child[LEFT] != NULL) ? LEFT : RIGHT]; if (curchild) /* we could have no children at all */ curchild->parent = redundant->parent; if (redundant->parent == NULL) aTree->index[index].root = curchild; else { if (redundant == redundant->parent->child[LEFT]) redundant->parent->child[LEFT] = curchild; else redundant->parent->child[RIGHT] = curchild; } if (redundant != curnode) { curnode->content = redundant->content; curnode->size = redundant->size; } if (isBlack(redundant)) { if (curchild == NULL) { if (redundant->parent) { Node temp; memset(&temp, '\0', sizeof(Node)); temp.parent = redundant->parent; temp.red = 0; TreeBalanceAfterRemove(aTree, &temp, index); } } else TreeBalanceAfterRemove(aTree, curchild, index); } #if defined(UNIT_TESTS) || defined(NO_HEAP_TRACKING) free(redundant); #else (aTree->heap_tracking) ? myfree(__FILE__, __LINE__, redundant) : free(redundant); #endif if (index == 0) { aTree->size -= size; --(aTree->count); } return content; } /** * Remove an item from a tree * @param aTree the list to which the item is to be added * @param curnode the list item content itself */ void* TreeRemoveIndex(Tree* aTree, void* content, int index) { Node* curnode = TreeFindContentIndex(aTree, content, index); if (curnode == NULL) return NULL; return TreeRemoveNodeIndex(aTree, curnode, index); } void* TreeRemove(Tree* aTree, void* content) { int i; void* rc = NULL; for (i = 0; i < aTree->indexes; ++i) rc = TreeRemoveIndex(aTree, content, i); return rc; } void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index) { Node* curnode = TreeFindIndex(aTree, key, index); void* content = NULL; int i; if (curnode == NULL) return NULL; content = TreeRemoveNodeIndex(aTree, curnode, index); for (i = 0; i < aTree->indexes; ++i) { if (i != index) content = TreeRemoveIndex(aTree, content, i); } return content; } void* TreeRemoveKey(Tree* aTree, void* key) { return TreeRemoveKeyIndex(aTree, key, 0); } int TreeIntCompare(void* a, void* b, int content) { int i = *((int*)a); int j = *((int*)b); /* printf("comparing %d %d\n", *((int*)a), *((int*)b)); */ return (i > j) ? -1 : (i == j) ? 0 : 1; } int TreePtrCompare(void* a, void* b, int content) { return (a > b) ? -1 : (a == b) ? 0 : 1; } int TreeStringCompare(void* a, void* b, int content) { return strcmp((char*)a, (char*)b); } #if defined(UNIT_TESTS) int check(Tree *t) { Node* curnode = NULL; int rc = 0; curnode = TreeNextElement(t, curnode); while (curnode) { Node* prevnode = curnode; curnode = TreeNextElement(t, curnode); if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content))) { printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content)); rc = 99; } } return rc; } int traverse(Tree *t, int lookfor) { Node* curnode = NULL; int rc = 0; printf("Traversing\n"); curnode = TreeNextElement(t, curnode); /* printf("content int %d\n", *(int*)(curnode->content)); */ while (curnode) { Node* prevnode = curnode; curnode = TreeNextElement(t, curnode); /* if (curnode) printf("content int %d\n", *(int*)(curnode->content)); */ if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content))) { printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content)); } if (curnode && (lookfor == *(int*)(curnode->content))) printf("missing item %d actually found\n", lookfor); } printf("End traverse %d\n", rc); return rc; } int test(int limit) { int i, *ip, *todelete; Node* current = NULL; Tree* t = TreeInitialize(TreeIntCompare); int rc = 0; printf("Tree initialized\n"); srand(time(NULL)); ip = malloc(sizeof(int)); *ip = 2; TreeAdd(t, (void*)ip, sizeof(int)); check(t); i = 2; void* result = TreeRemove(t, (void*)&i); if (result) free(result); int actual[limit]; for (i = 0; i < limit; i++) { void* replaced = NULL; ip = malloc(sizeof(int)); *ip = rand(); replaced = TreeAdd(t, (void*)ip, sizeof(int)); if (replaced) /* duplicate */ { free(replaced); actual[i] = -1; } else actual[i] = *ip; if (i==5) todelete = ip; printf("Tree element added %d\n", *ip); if (1 % 1000 == 0) { rc = check(t); printf("%d elements, check result %d\n", i+1, rc); if (rc != 0) return 88; } } check(t); for (i = 0; i < limit; i++) { int parm = actual[i]; if (parm == -1) continue; Node* found = TreeFind(t, (void*)&parm); if (found) printf("Tree find %d %d\n", parm, *(int*)(found->content)); else { printf("%d not found\n", parm); traverse(t, parm); return -2; } } check(t); for (i = limit -1; i >= 0; i--) { int parm = actual[i]; void *found; if (parm == -1) /* skip duplicate */ continue; found = TreeRemove(t, (void*)&parm); if (found) { printf("%d Tree remove %d %d\n", i, parm, *(int*)(found)); free(found); } else { int count = 0; printf("%d %d not found\n", i, parm); traverse(t, parm); for (i = 0; i < limit; i++) if (actual[i] == parm) ++count; printf("%d occurs %d times\n", parm, count); return -2; } if (i % 1000 == 0) { rc = check(t); printf("%d elements, check result %d\n", i+1, rc); if (rc != 0) return 88; } } printf("finished\n"); return 0; } int main(int argc, char *argv[]) { int rc = 0; while (rc == 0) rc = test(999999); } #endif