blob: 6ff5fb91bd176b0666475d1950520cfb43e2693c [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.editor.impl;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: cdr
*/
public abstract class RedBlackTree<K> {
public static boolean VERIFY = false;
private static final int INDENT_STEP = 4;
private int nodeSize; // number of nodes
protected int modCount;
protected Node<K> root;
public RedBlackTree() {
root = null;
verifyProperties();
}
protected void rotateLeft(Node<K> n) {
Node<K> r = n.getRight();
replaceNode(n, r);
n.setRight(r.getLeft());
if (r.getLeft() != null) {
r.getLeft().setParent(n);
}
r.setLeft(n);
n.setParent(r);
}
protected void rotateRight(Node<K> n) {
Node<K> l = n.getLeft();
replaceNode(n, l);
n.setLeft(l.getRight());
if (l.getRight() != null) {
l.getRight().setParent(n);
}
l.setRight(n);
n.setParent(l);
}
protected void replaceNode(@NotNull Node<K> oldn, Node<K> newn) {
Node<K> parent = oldn.getParent();
if (parent == null) {
root = newn;
}
else {
if (oldn == parent.getLeft()) {
parent.setLeft(newn);
}
else {
parent.setRight(newn);
}
}
if (newn != null) {
newn.setParent(parent);
}
//oldn.parent = null;
//oldn.left = null;
//oldn.right = null;
}
protected void onInsertNode() {
nodeSize++;
}
protected void insertCase1(Node<K> n) {
if (n.getParent() == null) {
n.setBlack();
}
else {
insertCase2(n);
}
}
private void insertCase2(Node<K> n) {
if (!isBlack(n.getParent())) {
insertCase3(n);
}
// Tree is still valid
}
private void insertCase3(Node<K> n) {
if (!isBlack(n.uncle())) {
n.getParent().setBlack();
n.uncle().setBlack();
n.grandparent().setRed();
insertCase1(n.grandparent());
}
else {
insertCase4(n);
}
}
private void insertCase4(Node<K> n) {
if (n == n.getParent().getRight() && n.getParent() == n.grandparent().getLeft()) {
rotateLeft(n.getParent());
n = n.getLeft();
}
else if (n == n.getParent().getLeft() && n.getParent() == n.grandparent().getRight()) {
rotateRight(n.getParent());
n = n.getRight();
}
insertCase5(n);
}
private void insertCase5(Node<K> n) {
n.getParent().setBlack();
n.grandparent().setRed();
if (n == n.getParent().getLeft() && n.getParent() == n.grandparent().getLeft()) {
rotateRight(n.grandparent());
}
else {
assert n == n.getParent().getRight();
assert n.getParent() == n.grandparent().getRight();
rotateLeft(n.grandparent());
}
}
private static <K> void assertParentChild(Node<K> node1) {
assert node1 == null || node1.getParent() == null || node1.getParent().getLeft() == node1 || node1.getParent().getRight() == node1;
}
protected void deleteNode(@NotNull Node<K> n) {
modCount++;
Node<K> e = n;
while (e.getParent() != null) e = e.getParent();
assert e == root : e; // assert the node belongs to our tree
if (n.getLeft() != null && n.getRight() != null) {
// Copy key/value from predecessor and then delete it instead
Node<K> pred = maximumNode(n.getLeft());
//Color c = pred.color;
//swap(n, pred);
//assert pred.color == c;
//pred.color = n.color;
//n.color = c;
n = swapWithMaxPred(n, pred);
}
assert n.getLeft() == null || n.getRight() == null;
Node<K> child = n.getRight() == null ? n.getLeft() : n.getRight();
if (isBlack(n)) {
n.setColor(isBlack(child));
deleteCase1(n);
}
replaceNode(n, child);
if (!isBlack(root)) {
root.setBlack();
}
assert nodeSize > 0 : nodeSize;
nodeSize--;
verifyProperties();
}
protected abstract Node<K> swapWithMaxPred(Node<K> nowAscendant, Node<K> nowDescendant);
protected Node<K> maximumNode(Node<K> n) {
assert n != null;
while (n.getRight() != null) {
n = n.getRight();
}
return n;
}
private void deleteCase1(Node<K> n) {
if (n.getParent() != null) {
deleteCase2(n);
}
}
private void deleteCase2(Node<K> n) {
if (!isBlack(n.sibling())) {
n.getParent().setRed();
n.sibling().setBlack();
if (n == n.getParent().getLeft()) {
rotateLeft(n.getParent());
}
else {
rotateRight(n.getParent());
}
}
deleteCase3(n);
}
private void deleteCase3(Node<K> n) {
if (isBlack(n.getParent()) &&
isBlack(n.sibling()) &&
isBlack(n.sibling().getLeft()) &&
isBlack(n.sibling().getRight())) {
n.sibling().setRed();
deleteCase1(n.getParent());
}
else {
deleteCase4(n);
}
}
private void deleteCase4(Node<K> n) {
if (!isBlack(n.getParent()) &&
isBlack(n.sibling()) &&
isBlack(n.sibling().getLeft()) &&
isBlack(n.sibling().getRight())) {
n.sibling().setRed();
n.getParent().setBlack();
}
else {
deleteCase5(n);
}
}
private void deleteCase5(Node<K> n) {
if (n == n.getParent().getLeft() &&
isBlack(n.sibling()) &&
!isBlack(n.sibling().getLeft()) &&
isBlack(n.sibling().getRight())) {
n.sibling().setRed();
n.sibling().getLeft().setBlack();
rotateRight(n.sibling());
}
else if (n == n.getParent().getRight() &&
isBlack(n.sibling()) &&
!isBlack(n.sibling().getRight()) &&
isBlack(n.sibling().getLeft())) {
n.sibling().setRed();
n.sibling().getRight().setBlack();
rotateLeft(n.sibling());
}
deleteCase6(n);
}
private void deleteCase6(Node<K> n) {
n.sibling().setColor(isBlack(n.getParent()));
n.getParent().setBlack();
if (n == n.getParent().getLeft()) {
assert !isBlack(n.sibling().getRight());
n.sibling().getRight().setBlack();
rotateLeft(n.getParent());
}
else {
assert !isBlack(n.sibling().getLeft());
n.sibling().getLeft().setBlack();
rotateRight(n.getParent());
}
}
public void print() {
printHelper(root, 0);
}
private static void printHelper(Node<?> n, int indent) {
if (n == null) {
System.err.print("<empty tree>");
return;
}
if (n.getRight() != null) {
printHelper(n.getRight(), indent + INDENT_STEP);
}
for (int i = 0; i < indent; i++) {
System.err.print(" ");
}
if (n.isBlack()) {
System.err.println(n);
}
else {
System.err.println("<" + n + ">");
}
if (n.getLeft() != null) {
printHelper(n.getLeft(), indent + INDENT_STEP);
}
}
public abstract static class Node<K> {
protected Node<K> left;
protected Node<K> right;
protected Node<K> parent = null;
private volatile byte myFlags;
protected static final int COLOR_FLAG = 0;
protected boolean isFlagSet(int flag) {
int state = myFlags >> flag;
return (state & 1) != 0;
}
protected void setFlag(int flag, boolean value) {
assert flag < 8;
int state = value ? 1 : 0;
myFlags = (byte)(myFlags & ~(1 << flag) | state << flag);
}
public Node<K> grandparent() {
assert getParent() != null; // Not the root node
assert getParent().getParent() != null; // Not child of root
return getParent().getParent();
}
public Node<K> sibling() {
Node<K> parent = getParent();
assert parent != null; // Root node has no sibling
return this == parent.getLeft() ? parent.getRight() : parent.getLeft();
}
public Node<K> uncle() {
assert getParent() != null; // Root node has no uncle
assert getParent().getParent() != null; // Children of root have no uncle
return getParent().sibling();
}
public Node<K> getLeft() {
return left;
}
public void setLeft(Node<K> left) {
this.left = left;
}
public Node<K> getRight() {
return right;
}
public void setRight(Node<K> right) {
this.right = right;
}
public Node<K> getParent() {
return parent;
}
public void setParent(Node<K> parent) {
this.parent = parent;
}
public abstract boolean processAliveKeys(@NotNull Processor<? super K> processor);
public abstract boolean hasAliveKey(boolean purgeDead);
public boolean isBlack() {
return isFlagSet(COLOR_FLAG);
}
public void setBlack() {
setFlag(COLOR_FLAG, true);
}
public void setRed() {
setFlag(COLOR_FLAG, false);
}
public void setColor(boolean isBlack) {
setFlag(COLOR_FLAG, isBlack);
}
}
public int size() {
return nodeSize;
}
public int nodeSize() {
return nodeSize;
}
public void verifyProperties() {
//if (true) return;
if (VERIFY) {
verifyProperty1(root);
verifyProperty2(root);
// Property 3 is implicit
verifyProperty4(root);
verifyProperty5(root);
}
}
private static void verifyProperty1(Node<?> n) {
assert !isBlack(n) || isBlack(n);
if (n == null) return;
assert n.getParent() != n;
assert n.getLeft() != n;
assert n.getRight() != n;
assertParentChild(n);
verifyProperty1(n.getLeft());
verifyProperty1(n.getRight());
}
private static void verifyProperty2(Node<?> root) {
assert isBlack(root);
}
private static boolean isBlack(@Nullable Node<?> n) {
return n == null || n.isBlack();
}
private static void verifyProperty4(Node<?> n) {
if (!isBlack(n)) {
assert isBlack(n.getLeft());
assert isBlack(n.getRight());
assert isBlack(n.getParent());
}
if (n == null) return;
verifyProperty4(n.getLeft());
verifyProperty4(n.getRight());
}
private static void verifyProperty5(Node<?> root) {
verifyProperty5Helper(root, 0, -1);
}
private static int verifyProperty5Helper(Node<?> n, int blackCount, int pathBlackCount) {
if (isBlack(n)) {
blackCount++;
}
if (n == null) {
if (pathBlackCount == -1) {
pathBlackCount = blackCount;
}
else {
assert blackCount == pathBlackCount;
}
return pathBlackCount;
}
pathBlackCount = verifyProperty5Helper(n.getLeft(), blackCount, pathBlackCount);
pathBlackCount = verifyProperty5Helper(n.getRight(), blackCount, pathBlackCount);
return pathBlackCount;
}
public void clear() {
modCount++;
root = null;
nodeSize = 0;
}
}