/* * 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; } }