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