Mercurial > projects > hoofbaby
diff src/impl/hoofbaby/util/redblack.d @ 0:3425707ddbf6
Initial import (hopefully this mercurial stuff works...)
author | fraserofthenight |
---|---|
date | Mon, 06 Jul 2009 08:06:28 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/impl/hoofbaby/util/redblack.d Mon Jul 06 08:06:28 2009 -0700 @@ -0,0 +1,937 @@ +/******************************************************************************* + + (this module taken from tango.util.container.RedBlack, since that + module is package-protected) + + copyright: Copyright (c) 2008 Kris Bell. All rights reserved + + license: BSD style: $(LICENSE) + + version: Apr 2008: Initial release + + authors: Kris, tsalm + + Since: 0.99.7 + + Based upon Doug Lea's Java collection package + +*******************************************************************************/ + +module candy.util.redblack; + +template Compare (V) { alias int function (ref V a, ref V b) Compare; } + +private typedef int AttributeDummy; + +/******************************************************************************* + + RedBlack implements basic capabilities of Red-Black trees, + an efficient kind of balanced binary tree. The particular + algorithms used are adaptations of those in Corman, + Lieserson, and Rivest's <EM>Introduction to Algorithms</EM>. + This class was inspired by (and code cross-checked with) a + similar class by Chuck McManis. The implementations of + rebalancings during insertion and deletion are + a little trickier than those versions since they + don't swap Cell contents or use special dummy nilnodes. + + Doug Lea + +*******************************************************************************/ + +struct RedBlack (V, A = AttributeDummy) +{ + alias RedBlack!(V, A) Type; + alias Type *Ref; + + enum : bool {RED = false, BLACK = true} + + /** + * Pointer to left child + **/ + + package Ref left; + + /** + * Pointer to right child + **/ + + package Ref right; + + /** + * Pointer to parent (null if root) + **/ + + package Ref parent; + + package V value; + + static if (!is(typeof(A) == AttributeDummy)) + { + A attribute; + } + + /** + * The node color (RED, BLACK) + **/ + + package bool color; + + static if (!is(typeof(A) == AttributeDummy)) + { + final Ref set (V v, A a) + { + attribute = a; + return set (v); + } + } + + /** + * Make a new Cell with given value, null links, and BLACK color. + * Normally only called to establish a new root. + **/ + + final Ref set (V v) + { + value = v; + left = null; + right = null; + parent = null; + color = BLACK; + return this; + } + + /** + * Return a new Ref with same value and color as self, + * but with null links. (Since it is never OK to have + * multiple identical links in a RB tree.) + **/ + + protected Ref dup (Ref delegate() alloc) + { + static if (is(typeof(A) == AttributeDummy)) + auto t = alloc().set (value); + else + auto t = alloc().set (value, attribute); + + t.color = color; + return t; + } + + + /** + * See_Also: tango.util.collection.model.View.View.checkImplementation. + **/ + public void checkImplementation () + { + + // It's too hard to check the property that every simple + // path from node to leaf has same number of black nodes. + // So restrict to the following + + assert(parent is null || + this is parent.left || + this is parent.right); + + assert(left is null || + this is left.parent); + + assert(right is null || + this is right.parent); + + assert(color is BLACK || + (colorOf(left) is BLACK) && (colorOf(right) is BLACK)); + + if (left !is null) + left.checkImplementation(); + if (right !is null) + right.checkImplementation(); + } + + /** + * Return the minimum value of the current (sub)tree + **/ + + Ref leftmost () + { + auto p = this; + for ( ; p.left; p = p.left) {} + return p; + } + + /** + * Return the maximum value of the current (sub)tree + **/ + Ref rightmost () + { + auto p = this; + for ( ; p.right; p = p.right) {} + return p; + } + + /** + * Return the root (parentless node) of the tree + **/ + Ref root () + { + auto p = this; + for ( ; p.parent; p = p.parent) {} + return p; + } + + /** + * Return true if node is a root (i.e., has a null parent) + **/ + + bool isRoot () + { + return parent is null; + } + + + /** + * Return the inorder successor, or null if no such + **/ + + Ref successor () + { + if (right) + return right.leftmost; + + auto p = parent; + auto ch = this; + while (p && ch is p.right) + { + ch = p; + p = p.parent; + } + return p; + } + + /** + * Return the inorder predecessor, or null if no such + **/ + + Ref predecessor () + { + if (left) + return left.rightmost; + + auto p = parent; + auto ch = this; + while (p && ch is p.left) + { + ch = p; + p = p.parent; + } + return p; + } + + /** + * Return the number of nodes in the subtree + **/ + int size () + { + auto c = 1; + if (left) + c += left.size; + if (right) + c += right.size; + return c; + } + + + /** + * Return node of current subtree containing value as value(), + * if it exists, else null. + * Uses Comparator cmp to find and to check equality. + **/ + + Ref find (V value, Compare!(V) cmp) + { + auto t = this; + for (;;) + { + auto diff = cmp (value, t.value); + if (diff is 0) + return t; + else + if (diff < 0) + t = t.left; + else + t = t.right; + if (t is null) + break; + } + return null; + } + + + /** + * Return node of subtree matching "value" + * or, if none found, the one just after or before + * if it doesn't exist, return null + * Uses Comparator cmp to find and to check equality. + **/ + Ref findFirst (V value, Compare!(V) cmp, bool after = true) + { + auto t = this; + auto tLower = this; + auto tGreater = this; + + for (;;) + { + auto diff = cmp (value, t.value); + if (diff is 0) + return t; + else + if (diff < 0) + { + tGreater = t; + t = t.left; + } + else + { + tLower = t; + t = t.right; + } + if (t is null) + break; + } + + if (after) + { + if (cmp (value, tGreater.value) <= 0) + if (cmp (value, tGreater.value) < 0) + return tGreater; + } + else + { + if (cmp (value, tLower.value) >= 0) + if (cmp (value, tLower.value) > 0) + return tLower; + } + + return null; + } + + /** + * Return number of nodes of current subtree containing value. + * Uses Comparator cmp to find and to check equality. + **/ + int count (V value, Compare!(V) cmp) + { + auto c = 0; + auto t = this; + while (t) + { + int diff = cmp (value, t.value); + if (diff is 0) + { + ++c; + if (t.left is null) + t = t.right; + else + if (t.right is null) + t = t.left; + else + { + c += t.right.count (value, cmp); + t = t.left; + } + } + else + if (diff < 0) + t = t.left; + else + t = t.right; + } + return c; + } + + static if (!is(typeof(A) == AttributeDummy)) + { + Ref findAttribute (A attribute, Compare!(A) cmp) + { + auto t = this; + + while (t) + { + if (t.attribute == attribute) + return t; + else + if (t.right is null) + t = t.left; + else + if (t.left is null) + t = t.right; + else + { + auto p = t.left.findAttribute (attribute, cmp); + + if (p !is null) + return p; + else + t = t.right; + } + } + return null; // not reached + } + + int countAttribute (A attrib, Compare!(A) cmp) + { + int c = 0; + auto t = this; + + while (t) + { + if (t.attribute == attribute) + ++c; + + if (t.right is null) + t = t.left; + else + if (t.left is null) + t = t.right; + else + { + c += t.left.countAttribute (attribute, cmp); + t = t.right; + } + } + return c; + } + + /** + * find and return a cell holding (key, element), or null if no such + **/ + Ref find (V value, A attribute, Compare!(V) cmp) + { + auto t = this; + + for (;;) + { + int diff = cmp (value, t.value); + if (diff is 0 && t.attribute == attribute) + return t; + else + if (diff <= 0) + t = t.left; + else + t = t.right; + + if (t is null) + break; + } + return null; + } + + } + + + + /** + * Return a new subtree containing each value of current subtree + **/ + + Ref copyTree (Ref delegate() alloc) + { + auto t = dup (alloc); + + if (left) + { + t.left = left.copyTree (alloc); + t.left.parent = t; + } + + if (right) + { + t.right = right.copyTree (alloc); + t.right.parent = t; + } + + return t; + } + + + /** + * There's no generic value insertion. Instead find the + * place you want to add a node and then invoke insertLeft + * or insertRight. + * <P> + * Insert Cell as the left child of current node, and then + * rebalance the tree it is in. + * @param Cell the Cell to add + * @param root, the root of the current tree + * Returns: the new root of the current tree. (Rebalancing + * can change the root!) + **/ + + + Ref insertLeft (Ref cell, Ref root) + { + left = cell; + cell.parent = this; + return cell.fixAfterInsertion (root); + } + + /** + * Insert Cell as the right child of current node, and then + * rebalance the tree it is in. + * @param Cell the Cell to add + * @param root, the root of the current tree + * Returns: the new root of the current tree. (Rebalancing + * can change the root!) + **/ + + Ref insertRight (Ref cell, Ref root) + { + right = cell; + cell.parent = this; + return cell.fixAfterInsertion (root); + } + + + /** + * Delete the current node, and then rebalance the tree it is in + * @param root the root of the current tree + * Returns: the new root of the current tree. (Rebalancing + * can change the root!) + **/ + + + Ref remove (Ref root) + { + // handle case where we are only node + if (left is null && right is null && parent is null) + return null; + + // if strictly internal, swap places with a successor + if (left && right) + { + auto s = successor; + + // To work nicely with arbitrary subclasses of Ref, we don't want to + // just copy successor's fields. since we don't know what + // they are. Instead we swap positions _in the tree. + root = swapPosition (this, s, root); + } + + // Start fixup at replacement node (normally a child). + // But if no children, fake it by using self + + if (left is null && right is null) + { + if (color is BLACK) + root = this.fixAfterDeletion (root); + + // Unlink (Couldn't before since fixAfterDeletion needs parent ptr) + if (parent) + { + if (this is parent.left) + parent.left = null; + else + if (this is parent.right) + parent.right = null; + parent = null; + } + } + else + { + auto replacement = left; + if (replacement is null) + replacement = right; + + // link replacement to parent + replacement.parent = parent; + + if (parent is null) + root = replacement; + else + if (this is parent.left) + parent.left = replacement; + else + parent.right = replacement; + + left = null; + right = null; + parent = null; + + // fix replacement + if (color is BLACK) + root = replacement.fixAfterDeletion (root); + } + return root; + } + + /** + * Swap the linkages of two nodes in a tree. + * Return new root, in case it changed. + **/ + + static Ref swapPosition (Ref x, Ref y, Ref root) + { + /* Too messy. TODO: find sequence of assigments that are always OK */ + + auto px = x.parent; + bool xpl = px !is null && x is px.left; + auto lx = x.left; + auto rx = x.right; + + auto py = y.parent; + bool ypl = py !is null && y is py.left; + auto ly = y.left; + auto ry = y.right; + + if (x is py) + { + y.parent = px; + if (px !is null) + if (xpl) + px.left = y; + else + px.right = y; + + x.parent = y; + if (ypl) + { + y.left = x; + y.right = rx; + if (rx !is null) + rx.parent = y; + } + else + { + y.right = x; + y.left = lx; + if (lx !is null) + lx.parent = y; + } + + x.left = ly; + if (ly !is null) + ly.parent = x; + + x.right = ry; + if (ry !is null) + ry.parent = x; + } + else + if (y is px) + { + x.parent = py; + if (py !is null) + if (ypl) + py.left = x; + else + py.right = x; + + y.parent = x; + if (xpl) + { + x.left = y; + x.right = ry; + if (ry !is null) + ry.parent = x; + } + else + { + x.right = y; + x.left = ly; + if (ly !is null) + ly.parent = x; + } + + y.left = lx; + if (lx !is null) + lx.parent = y; + + y.right = rx; + if (rx !is null) + rx.parent = y; + } + else + { + x.parent = py; + if (py !is null) + if (ypl) + py.left = x; + else + py.right = x; + + x.left = ly; + if (ly !is null) + ly.parent = x; + + x.right = ry; + if (ry !is null) + ry.parent = x; + + y.parent = px; + if (px !is null) + if (xpl) + px.left = y; + else + px.right = y; + + y.left = lx; + if (lx !is null) + lx.parent = y; + + y.right = rx; + if (rx !is null) + rx.parent = y; + } + + bool c = x.color; + x.color = y.color; + y.color = c; + + if (root is x) + root = y; + else + if (root is y) + root = x; + return root; + } + + + + /** + * Return color of node p, or BLACK if p is null + * (In the CLR version, they use + * a special dummy `nil' node for such purposes, but that doesn't + * work well here, since it could lead to creating one such special + * node per real node.) + * + **/ + + static bool colorOf (Ref p) + { + return (p is null) ? BLACK : p.color; + } + + /** + * return parent of node p, or null if p is null + **/ + static Ref parentOf (Ref p) + { + return (p is null) ? null : p.parent; + } + + /** + * Set the color of node p, or do nothing if p is null + **/ + + static void setColor (Ref p, bool c) + { + if (p !is null) + p.color = c; + } + + /** + * return left child of node p, or null if p is null + **/ + + static Ref leftOf (Ref p) + { + return (p is null) ? null : p.left; + } + + /** + * return right child of node p, or null if p is null + **/ + + static Ref rightOf (Ref p) + { + return (p is null) ? null : p.right; + } + + + /** From CLR **/ + package Ref rotateLeft (Ref root) + { + auto r = right; + right = r.left; + + if (r.left) + r.left.parent = this; + + r.parent = parent; + if (parent is null) + root = r; + else + if (parent.left is this) + parent.left = r; + else + parent.right = r; + + r.left = this; + parent = r; + return root; + } + + /** From CLR **/ + package Ref rotateRight (Ref root) + { + auto l = left; + left = l.right; + + if (l.right !is null) + l.right.parent = this; + + l.parent = parent; + if (parent is null) + root = l; + else + if (parent.right is this) + parent.right = l; + else + parent.left = l; + + l.right = this; + parent = l; + return root; + } + + + /** From CLR **/ + package Ref fixAfterInsertion (Ref root) + { + color = RED; + auto x = this; + + while (x && x !is root && x.parent.color is RED) + { + if (parentOf(x) is leftOf(parentOf(parentOf(x)))) + { + auto y = rightOf(parentOf(parentOf(x))); + if (colorOf(y) is RED) + { + setColor(parentOf(x), BLACK); + setColor(y, BLACK); + setColor(parentOf(parentOf(x)), RED); + x = parentOf(parentOf(x)); + } + else + { + if (x is rightOf(parentOf(x))) + { + x = parentOf(x); + root = x.rotateLeft(root); + } + + setColor(parentOf(x), BLACK); + setColor(parentOf(parentOf(x)), RED); + if (parentOf(parentOf(x)) !is null) + root = parentOf(parentOf(x)).rotateRight(root); + } + } + else + { + auto y = leftOf(parentOf(parentOf(x))); + if (colorOf(y) is RED) + { + setColor(parentOf(x), BLACK); + setColor(y, BLACK); + setColor(parentOf(parentOf(x)), RED); + x = parentOf(parentOf(x)); + } + else + { + if (x is leftOf(parentOf(x))) + { + x = parentOf(x); + root = x.rotateRight(root); + } + + setColor(parentOf(x), BLACK); + setColor(parentOf(parentOf(x)), RED); + if (parentOf(parentOf(x)) !is null) + root = parentOf(parentOf(x)).rotateLeft(root); + } + } + } + root.color = BLACK; + return root; + } + + + + /** From CLR **/ + package Ref fixAfterDeletion(Ref root) + { + auto x = this; + while (x !is root && colorOf(x) is BLACK) + { + if (x is leftOf(parentOf(x))) + { + auto sib = rightOf(parentOf(x)); + if (colorOf(sib) is RED) + { + setColor(sib, BLACK); + setColor(parentOf(x), RED); + root = parentOf(x).rotateLeft(root); + sib = rightOf(parentOf(x)); + } + if (colorOf(leftOf(sib)) is BLACK && colorOf(rightOf(sib)) is BLACK) + { + setColor(sib, RED); + x = parentOf(x); + } + else + { + if (colorOf(rightOf(sib)) is BLACK) + { + setColor(leftOf(sib), BLACK); + setColor(sib, RED); + root = sib.rotateRight(root); + sib = rightOf(parentOf(x)); + } + + setColor(sib, colorOf(parentOf(x))); + setColor(parentOf(x), BLACK); + setColor(rightOf(sib), BLACK); + root = parentOf(x).rotateLeft(root); + x = root; + } + } + else + { + auto sib = leftOf(parentOf(x)); + if (colorOf(sib) is RED) + { + setColor(sib, BLACK); + setColor(parentOf(x), RED); + root = parentOf(x).rotateRight(root); + sib = leftOf(parentOf(x)); + } + + if (colorOf(rightOf(sib)) is BLACK && colorOf(leftOf(sib)) is BLACK) + { + setColor(sib, RED); + x = parentOf(x); + } + else + { + if (colorOf(leftOf(sib)) is BLACK) + { + setColor(rightOf(sib), BLACK); + setColor(sib, RED); + root = sib.rotateLeft(root); + sib = leftOf(parentOf(x)); + } + + setColor(sib, colorOf(parentOf(x))); + setColor(parentOf(x), BLACK); + setColor(leftOf(sib), BLACK); + root = parentOf(x).rotateRight(root); + x = root; + } + } + } + + setColor(x, BLACK); + return root; + } +}