/* jCAE stand for Java Computer Aided Engineering. Features are : Small CAD
modeler, Finite element mesher, Plugin architecture.
Copyright (C) 2006, by EADS CRC
Copyright (C) 2007,2008, by EADS France
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
package org.jcae.mesh.amibe.util;
import java.util.Iterator;
import java.util.logging.Logger;
/**
* Red-black binary trees to store quality factors.
* Main ideas come from Ben Pfaff's <a href="http://adtinfo.org/">GNU libavl</a>.
* These trees are used to sort vertices, edges, or triangles according
* to their quality factors, and to process them in increasing or decreasing
* order after they have been sorted. See examples in algorithms from
* {@link org.jcae.mesh.amibe.algos3d}.
* A red-black tree has the following properties:
* <ol>
* <li>Null nodes are black.</li>
* <li>A red node has no red child.</li>
* <li>Paths from any node to external leaves contain the same number of black
* nodes.</li>
* </ol>
* By convention, root node is always black in order to simplify node insertion
* and removal. Node insertions and removals are explained in detail at
* <a href="http://en.wikipedia.org/wiki/Red-Black_tree">wikipedia</a>.
*/
public class PRedBlackSortedTree<E> extends QSortedTree<E>
{
private static final long serialVersionUID = 4767412412814775447L;
private static final Logger logger=Logger.getLogger(PRedBlackSortedTree.class.getName());
private static class Node<E> extends QSortedTree.Node<E>
{
private static final long serialVersionUID = 4887055383474718211L;
private boolean isRed;
@SuppressWarnings("unchecked")
@Override
protected Node<E> [] newChilds()
{
return new Node[2];
}
private Node(E o, double v)
{
super(o, v);
isRed = true;
}
@Override
public void reset(double v)
{
super.reset(v);
isRed = true;
}
@Override
public final String toString()
{
return super.toString()+" "+(isRed ? "red" : "black");
}
}
@Override
final QSortedTree.Node<E> newNode(E o, double v)
{
return new Node<E>(o, v);
}
// Helper function
private static <E> boolean isRedNode(QSortedTree.Node<E> x)
{
return (x != null) && ((Node<E>) x).isRed;
}
@Override
final boolean insertNode(QSortedTree.Node<E> o)
{
Node<E> p = (Node<E>) o;
Node<E> current = (Node<E>) root.child[0];
Node<E> q = (Node<E>) root;
int lastDir = 0;
while (current != null)
{
if (p.compareTo(current) < 0)
lastDir = 0;
else
lastDir = 1;
q = current;
current = (Node<E>) current.child[lastDir];
}
// Insert node
q.child[lastDir] = p;
p.parent = q;
// Node color is red, so property 3 is preserved.
// We must check if property 2 is violated, in which
// case our tree has to be rebalanced and/or repainted.
// Case I1: root node
if (q == root)
{
// We enforce root node to be black, this eases
// other cases below.
logger.fine("Case I1");
p.isRed = false;
assert !((Node<E>) root.child[0]).isRed;
return true;
}
while (p != root.child[0])
{
q = (Node<E>) p.parent;
// If parent is black, property 2 is preserved,
// everything is fine.
// Case I2: parent is black
if (!q.isRed)
{
logger.fine("Case I2");
assert !((Node<E>) root.child[0]).isRed;
return true;
}
// Parent is red, so it cannot be the root tree,
// and grandparent is black.
Node<E> grandparent = (Node<E>) q.parent;
assert grandparent != root;
if (grandparent.child[0] == q)
lastDir = 0;
else
lastDir = 1;
int sibDir = 1 - lastDir;
Node<E> uncle = (Node<E>) grandparent.child[sibDir];
if (isRedNode(uncle))
{
// Case I3: uncle is red
/* Paint nodes and continue from grandparent
gB gR
/ \ ------> / \
qR uR qB uB
\ \
pR pR
*/
logger.fine("Case I3");
q.isRed = false;
uncle.isRed = false;
grandparent.isRed = true;
p = grandparent;
}
else
{
assert !isRedNode(uncle);
if (q.child[lastDir] != p)
{
/* Rotate to put red nodes on the
same side
gB gB
/ \ ----> / \
qR uB pR uB
\ /
pR qR
*/
logger.fine("Case I4");
if (lastDir == 0)
grandparent.child[0] = q.rotateL();
else
grandparent.child[1] = q.rotateR();
p = q;
q = (Node<E>) p.parent;
}
/* Rotate on opposite way and recolor. Either
uncle is null, or we come from case 3 and
current node has 2 black children.
gB qB
/ \ ------> / \
qR uB pR gR
/ \ / \ / \
pR zB xB yB zB uB
/ \
xB yB
*/
assert (uncle == null && q.child[sibDir] == null &&
p.child[0] == null && p.child[1] == null) ||
(uncle != null && q.child[sibDir] != null &&
p.child[0] != null && p.child[1] != null);
logger.fine("Case I5");
Node<E> greatgrandparent = (Node<E>) grandparent.parent;
grandparent.isRed = true;
q.isRed = false;
if (greatgrandparent.child[0] == grandparent)
lastDir = 0;
else
lastDir = 1;
// lastDir has been modified, so use sibDir here
if (sibDir == 1)
greatgrandparent.child[lastDir] = grandparent.rotateR();
else
greatgrandparent.child[lastDir] = grandparent.rotateL();
assert !((Node<E>) root.child[0]).isRed;
return true;
}
}
((Node<E>) root.child[0]).isRed = false;
assert isValid();
return true;
}
@Override
final QSortedTree.Node<E> removeNode(QSortedTree.Node<E> o)
{
Node<E> p = (Node<E>) o;
Node<E> ret = p;
Node<E> q = (Node<E>) p.parent;
int lastDir = 0;
if (q.child[1] == p)
lastDir = 1;
if (p.child[1] == null)
{
q.child[lastDir] = p.child[0];
if (q.child[lastDir] != null)
q.child[lastDir].parent = q;
}
else
{
// p has a right child. Replace p by its
// successor, and update q and lastDir.
Node<E> r = (Node<E>) p.nextNode();
// Do not modify p's color!
p.swap(r);
p = r;
q = (Node<E>) p.parent;
if (q.child[0] == p)
lastDir = 0;
else
lastDir = 1;
assert p.child[0] == null;
q.child[lastDir] = p.child[1];
if (q.child[lastDir] != null)
q.child[lastDir].parent = q;
ret = r;
}
// p is the node to be removed, q its parent and
// lastDir so that q.child[lastDir] == p;
if (p.isRed)
return ret;
// p is a black node, so q is no more balanced, its
// lastDir child of q has 1 black node less than its
// sibling.
while (true)
{
p = (Node<E>) q.child[lastDir];
if (isRedNode(p))
{
p.isRed = false;
logger.fine("Red node :-)");
break;
}
// Case R1: root tree
if (q == root)
{
// All paths have one less black node
logger.fine("Case R1");
break;
}
int sibDir = 1 - lastDir;
Node<E> grandparent = (Node<E>) q.parent;
int gLastDir = 0;
if (grandparent.child[1] == q)
gLastDir = 1;
Node<E> sibling = (Node<E>) q.child[sibDir];
// A black node is removed, so its sibling cannot
// be null.
assert sibling != null : q.toString();
if (sibling.isRed)
{
// Case R2: sibling is red
logger.fine("Case R2");
sibling.isRed = false;
q.isRed = true;
assert sibling.child[0] != null;
assert sibling.child[1] != null;
/* Example with lastDir == 0
qB qR sB => gB
/ \ ----> / \ ----> / \
pB sR pB sB qR bB
/ \ / \ / \
aB bB aB bB pB aB => sB
*/
if (lastDir == 0)
grandparent.child[gLastDir] = q.rotateL();
else
grandparent.child[gLastDir] = q.rotateR();
grandparent = sibling;
gLastDir = lastDir;
sibling = (Node<E>) q.child[sibDir];
}
// Now sibling is black
assert !sibling.isRed;
if (!q.isRed && !isRedNode(sibling.child[0]) && !isRedNode(sibling.child[1]))
{
// Case R3: parent, sibling and sibling's
// children are black
logger.fine("Case R3");
sibling.isRed = true;
}
else
{
if (!isRedNode(sibling.child[0]) && !isRedNode(sibling.child[1]))
{
// Case R4: sibling and sibling's
// children are black, but parent is
// red.
assert q.isRed;
logger.fine("Case R4");
sibling.isRed = true;
q.isRed = false;
break;
}
if (isRedNode(sibling.child[lastDir]) && !isRedNode(sibling.child[sibDir]))
{
// Case R5: sibling is black, left child is
// red and right child is black.
// Rotate at sibling and paint nodes
// so that sibling.child[sibDir] is red.
logger.fine("Case R5");
Node<E> y = (Node<E>) sibling.child[lastDir];
y.isRed = false;
sibling.isRed = true;
if (lastDir == 0)
q.child[sibDir] = sibling.rotateR();
else
q.child[sibDir] = sibling.rotateL();
sibling = y;
/* Example with lastDir == 0
q* q*
/ \ ---> / \
xB sB xB yB
/ \ / \
yR zB aB sR
/ \ / \
aB bB bB zB
*/
}
logger.fine("Case R6");
// Case R6: sibling is black and its right child
// is red.
/*
q* qB s*
/ \ ---> / \ ---> / \
xB sB xB s* qB yB
/ \ / \ / \ / \
aB yR aB yB xB aB bB zB
/ \ / \
bB zB bB zB
*/
assert !isRedNode(sibling) && isRedNode(sibling.child[sibDir]);
sibling.isRed = q.isRed;
q.isRed = false;
((Node<E>) sibling.child[sibDir]).isRed = false;
if (lastDir == 0)
grandparent.child[gLastDir] = q.rotateL();
else
grandparent.child[gLastDir] = q.rotateR();
break;
}
if (q.parent.child[0] == q)
lastDir = 0;
else
lastDir = 1;
q = (Node<E>) q.parent;
}
assert isValid();
return ret;
}
private boolean isValid()
{
// Call debugIsValid() only when debugging, otherwise
// tree manipulations are way too slow.
return true;
//return debugIsValid();
//return checkParentPointers();
}
public boolean checkParentPointers()
{
if (root.child[0] == null)
return true;
for (Iterator<QSortedTree.Node<E>> it = iterator(); it.hasNext(); )
{
QSortedTree.Node<E> current = it.next();
if (current.child[0] != null && current.child[0].parent != current)
return false;
if (current.child[1] != null && current.child[1].parent != current)
return false;
}
return true;
}
public boolean debugIsValid()
{
Node<E> current = (Node<E>) root.child[0];
if (isRedNode(current))
return false;
if (current == null)
return true;
while (current.child[0] != null)
current = (Node<E>) current.child[0];
// Now traverse the tree
while (current != root)
{
if (isRedNode(current) && (isRedNode(current.child[0]) || isRedNode(current.child[1])))
return false;
if (current.child[0] != null && current.child[0].compareTo(current) > 0)
return false;
if (current.child[1] != null && current.child[1].compareTo(current) < 0)
return false;
if (current.child[1] != null)
{
current = (Node<E>) current.child[1];
if (isRedNode(current) && (isRedNode(current.child[0]) || isRedNode(current.child[1])))
return false;
while (current.child[0] != null)
{
current = (Node<E>) current.child[0];
if (isRedNode(current) && (isRedNode(current.child[0]) || isRedNode(current.child[1])))
return false;
}
}
else
{
// Walk upwards
while (current.parent.child[0] != current)
{
if (isRedNode(current) && (isRedNode(current.child[0]) || isRedNode(current.child[1])))
return false;
current = (Node<E>) current.parent;
}
current = (Node<E>) current.parent;
}
}
return true;
}
}