blob: b90fead215333932ee2c957ff22f8599d59fe4ac [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.uima.internal.util.rb_trees;
import java.util.NoSuchElementException;
import java.util.Random;
import org.apache.uima.internal.util.ComparableIntIterator;
import org.apache.uima.internal.util.ComparableIntPointerIterator;
import org.apache.uima.internal.util.IntArrayUtils;
import org.apache.uima.internal.util.IntComparator;
import org.apache.uima.internal.util.IntListIterator;
import org.apache.uima.internal.util.IntPointerIterator;
import org.apache.uima.internal.util.StringUtils;
/**
* Red-black tree implementation based on integer arrays. Preliminary performance measurements on
* j2se 1.4 indicate that the performance improvement as opposed to an object-based implementation
* are miniscule. This seems to indicate a much improved object creation handling in this vm.
*
* <p>
* This tree implementation knows two modes of insertion: keys that are already in the tree can be
* rejected, or inserted as duplicates. Duplicate key insertion is randomized so that the tree's
* performance degrades gracefully in the presence of many identical keys.
*
*
*/
public class IntArrayRBT {
/**
* Implement a comparable iterator over the keys.
*
*
*/
private class ComparablePointerIterator extends PointerIterator implements
ComparableIntPointerIterator {
private final IntComparator comp;
private int modificationSnapshot; // to catch illegal modifications
private int[] detectIllegalIndexUpdates; // shared copy with Index Repository
private int typeCode;
public boolean isConcurrentModification() {
return modificationSnapshot != detectIllegalIndexUpdates[typeCode];
}
public void resetConcurrentModification() {
modificationSnapshot = detectIllegalIndexUpdates[typeCode];
}
private ComparablePointerIterator(IntComparator comp) {
super();
this.comp = comp;
}
/**
* @see java.lang.Comparable#compareTo(Object)
*/
public int compareTo(Object o) throws NoSuchElementException {
ComparableIntPointerIterator it = (ComparableIntPointerIterator) o;
// assert(this.comp != null);
return this.comp.compare(get(), it.get());
}
public Object copy() {
ComparablePointerIterator copy = new ComparablePointerIterator(this.comp);
copy.currentNode = this.currentNode;
return copy;
}
}
/**
* Class comment for IntArrayRBT.java goes here.
*
*
*/
private class PointerIterator implements IntPointerIterator {
protected int currentNode;
private PointerIterator() {
super();
moveToFirst();
}
/**
* @see org.apache.uima.internal.util.IntPointerIterator#dec()
*/
public void dec() {
this.currentNode = previousNode(this.currentNode);
}
/**
* @see org.apache.uima.internal.util.IntPointerIterator#get()
*/
public int get() {
if (!isValid()) {
throw new NoSuchElementException();
}
return IntArrayRBT.this.key[this.currentNode];
}
/**
* @see org.apache.uima.internal.util.IntPointerIterator#inc()
*/
public void inc() {
this.currentNode = nextNode(this.currentNode);
}
/**
* @see org.apache.uima.internal.util.IntPointerIterator#isValid()
*/
public boolean isValid() {
return (this.currentNode != NIL);
}
/**
* @see org.apache.uima.internal.util.IntPointerIterator#moveToFirst()
*/
public void moveToFirst() {
this.currentNode = getFirstNode();
}
/**
* @see org.apache.uima.internal.util.IntPointerIterator#moveToLast()
*/
public void moveToLast() {
this.currentNode = IntArrayRBT.this.greatestNode;
}
/**
* @see org.apache.uima.internal.util.IntPointerIterator#copy()
*/
public Object copy() {
PointerIterator it = new PointerIterator();
it.currentNode = this.currentNode;
return it;
}
/**
* @see org.apache.uima.internal.util.IntPointerIterator#moveTo(int)
*/
public void moveTo(int i) {
this.currentNode = findInsertionPoint(i);
}
}
private class IntArrayRBTKeyIterator implements IntListIterator {
protected int currentNode;
protected IntArrayRBTKeyIterator() {
super();
this.currentNode = NIL;
}
public final boolean hasNext() {
return (this.currentNode != IntArrayRBT.this.greatestNode);
}
public final int next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
this.currentNode = nextNode(this.currentNode);
return IntArrayRBT.this.key[this.currentNode];
}
/**
* @see org.apache.uima.internal.util.IntListIterator#hasPrevious()
*/
public boolean hasPrevious() {
return (this.currentNode != NIL);
}
/**
* @see org.apache.uima.internal.util.IntListIterator#previous()
*/
public int previous() {
if (!hasPrevious()) {
throw new NoSuchElementException();
}
final int currentKey = IntArrayRBT.this.key[this.currentNode];
if (this.currentNode == getFirstNode()) {
this.currentNode = NIL;
} else {
this.currentNode = previousNode(this.currentNode);
}
return currentKey;
}
/**
* @see org.apache.uima.internal.util.IntListIterator#moveToEnd()
*/
public void moveToEnd() {
this.currentNode = IntArrayRBT.this.greatestNode;
}
/**
* @see org.apache.uima.internal.util.IntListIterator#moveToStart()
*/
public void moveToStart() {
this.currentNode = NIL;
}
protected final int getKey(int node) {
return IntArrayRBT.this.key[node];
}
}
private class ComparableIterator extends IntArrayRBTKeyIterator implements ComparableIntIterator {
private final IntComparator comparator;
private ComparableIterator(IntComparator comp) {
super();
this.comparator = comp;
}
/**
* @see java.lang.Comparable#compareTo(Object)
*/
public int compareTo(Object o) {
ComparableIterator it = (ComparableIterator) o;
return this.comparator.compare(IntArrayRBT.this.key[this.currentNode], it
.getKey(it.currentNode));
}
}
// Keys.
protected int[] key;
// Left daughters.
protected int[] left;
// Right daughters.
protected int[] right;
// Parents.
protected int[] parent;
// Colors.
protected boolean[] color;
// The index of the next node.
private int next;
// The current size of the tree. Since we can remove nodes, since needs to
// be kept separate from the next free cell.
private int size;
// The root of the tree.
protected int root;
// Keep a pointer to the largest node around so we can optimize for
// inserting
// keys that are larger than all keys already in the tree.
protected int greatestNode;
protected static final int default_size = 1024;
private static final int default_growth_factor = 2;
private static final int default_multiplication_limit = 2000000;
private int growth_factor;
private int multiplication_limit;
// The NIL sentinel
public static final int NIL = 0;
// The colors.
protected static final boolean red = true;
protected static final boolean black = false;
// Random number generator to randomize inserts of identical keys.
protected final Random rand;
/**
* Constructor for IntArrayRBT.
*/
public IntArrayRBT() {
this(default_size);
}
public IntArrayRBT(int initialSize) {
super();
this.rand = new Random();
if (initialSize < 1) {
initialSize = 1;
}
initVars();
// Increase initialSize by one since we use one slot for sentinel.
++initialSize;
this.growth_factor = default_growth_factor;
this.multiplication_limit = default_multiplication_limit;
// Init the arrays.
this.key = new int[initialSize];
this.left = new int[initialSize];
this.right = new int[initialSize];
this.parent = new int[initialSize];
this.color = new boolean[initialSize];
this.left[NIL] = NIL;
this.right[NIL] = NIL;
this.parent[NIL] = NIL;
this.color[NIL] = black;
}
private void initVars() {
this.root = NIL;
this.greatestNode = NIL;
this.next = 1;
this.size = 0;
}
public void flush() {
// All we do for flush is set the root to NIL and the size to 0.
initVars();
}
public final int size() {
return this.size;
}
private void grow(int initialSize) {
this.key = grow(this.key, initialSize);
this.left = grow(this.left, initialSize);
this.right = grow(this.right, initialSize);
this.parent = grow(this.parent, initialSize);
this.color = grow(this.color, initialSize);
}
protected int treeInsert(int k) {
int x = this.root;
int y, z;
if ((this.greatestNode != NIL) && (this.key[this.greatestNode] < k)) {
y = this.greatestNode;
z = newNode(k);
this.greatestNode = z;
} else {
y = NIL;
int xKey;
while (x != NIL) {
y = x;
xKey = this.key[x];
if (k < xKey) {
x = this.left[x];
} else if (k == xKey) {
return -x;
} else { // k == key[x]
x = this.right[x];
}
}
// The key was not found, so we create a new node, inserting the
// key.
z = newNode(k);
}
if (y == NIL) {
setAsRoot(z);
this.greatestNode = z;
this.parent[z] = NIL;
} else {
this.parent[z] = y;
if (k < this.key[y]) {
this.left[y] = z;
} else {
this.right[y] = z;
}
}
return z;
}
protected int treeInsertWithDups(int k) {
int x = this.root;
int y, z;
if ((this.greatestNode != NIL) && (this.key[this.greatestNode] <= k)) {
y = this.greatestNode;
z = newNode(k);
this.greatestNode = z;
this.right[y] = z;
this.parent[z] = y;
return z;
}
y = NIL;
int xKey;
while (x != NIL) {
y = x;
xKey = this.key[x];
if (k < xKey) {
x = this.left[x];
} else if (k > xKey) {
x = this.right[x];
} else { // k == key[x]
// Randomly search to the left or right.
if (this.rand.nextBoolean()) {
x = this.left[x];
} else {
x = this.right[x];
}
}
}
z = newNode(k);
if (y == NIL) {
setAsRoot(z);
this.greatestNode = z;
this.parent[z] = NIL;
} else {
this.parent[z] = y;
if (k < this.key[y]) {
this.left[y] = z;
} else if (k > this.key[y]) {
this.right[y] = z;
} else { // k == key[y]
// Randomly insert node to the left or right.
if (this.rand.nextBoolean()) {
this.left[y] = z;
} else {
this.right[y] = z;
}
}
}
return z;
}
protected int newNode(int k) {
// Make sure the tree is big enough to accomodate a new node.
if (this.next >= this.key.length) {
grow(this.next + 1);
}
// assert(key.length > next);
final int z = this.next;
++this.next;
++this.size;
this.key[z] = k;
this.left[z] = NIL;
this.right[z] = NIL;
this.color[z] = red;
return z;
}
private final void setAsRoot(int x) {
this.root = x;
this.parent[this.root] = NIL;
}
private final int[] grow(int[] array, int newSize) {
return IntArrayUtils.ensure_size(array, newSize, this.growth_factor, this.multiplication_limit);
}
private final boolean[] grow(boolean[] array, int newSize) {
return IntArrayUtils.ensure_size(array, newSize, this.growth_factor, this.multiplication_limit);
}
private final void leftRotate(int x) {
final int y = this.right[x];
this.right[x] = this.left[y];
if (this.left[y] != NIL) {
this.parent[this.left[y]] = x;
}
this.parent[y] = this.parent[x];
if (this.root == x) {
setAsRoot(y);
} else {
if (x == this.left[this.parent[x]]) {
this.left[this.parent[x]] = y;
} else {
this.right[this.parent[x]] = y;
}
}
this.left[y] = x;
this.parent[x] = y;
}
private final void rightRotate(int x) {
final int y = this.left[x];
this.left[x] = this.right[y];
if (this.right[y] != NIL) {
this.parent[this.right[y]] = x;
}
this.parent[y] = this.parent[x];
if (this.root == x) {
setAsRoot(y);
} else {
if (x == this.right[this.parent[x]]) {
this.right[this.parent[x]] = y;
} else {
this.left[this.parent[x]] = y;
}
}
this.right[y] = x;
this.parent[x] = y;
}
public int insertKey(int k) {
return insertKey(k, false);
}
public int insertKeyWithDups(int k) {
return insertKey(k, true);
}
private int insertKey(int k, boolean withDups) {
int x;
if (this.root == NIL) {
x = newNode(k);
setAsRoot(x);
this.color[this.root] = black;
this.greatestNode = x;
return x;
}
if (withDups) {
x = treeInsertWithDups(k);
} else {
x = treeInsert(k);
if (x < NIL) {
return -x;
}
}
this.color[x] = red;
int y;
final int node = x;
while ((x != this.root) && (this.color[this.parent[x]] == red)) {
if (this.parent[x] == this.left[this.parent[this.parent[x]]]) {
y = this.right[this.parent[this.parent[x]]];
if (this.color[y] == red) {
this.color[this.parent[x]] = black;
this.color[y] = black;
this.color[this.parent[this.parent[x]]] = red;
x = this.parent[this.parent[x]];
} else {
if (x == this.right[this.parent[x]]) {
x = this.parent[x];
leftRotate(x);
}
this.color[this.parent[x]] = black;
this.color[this.parent[this.parent[x]]] = red;
rightRotate(this.parent[this.parent[x]]);
}
} else {
y = this.left[this.parent[this.parent[x]]];
if (this.color[y] == red) {
this.color[this.parent[x]] = black;
this.color[y] = black;
this.color[this.parent[this.parent[x]]] = red;
x = this.parent[this.parent[x]];
} else {
if (x == this.left[this.parent[x]]) {
x = this.parent[x];
rightRotate(x);
}
this.color[this.parent[x]] = black;
this.color[this.parent[this.parent[x]]] = red;
leftRotate(this.parent[this.parent[x]]);
}
}
}
this.color[this.root] = black;
return node;
}
// private final boolean isNewNode(int node) {
// return (node == (next - 1));
// }
public int findKey(int k) {
int node = this.root;
while (node != NIL) {
if (k < this.key[node]) {
node = this.left[node];
} else if (k == this.key[node]) {
return node;
} else {
node = this.right[node];
}
}
// node == NIL
return NIL;
}
/**
* Find the node such that key[node] >= k and key[previous(node)] < k.
*/
public int findInsertionPoint(int k) {
int node = this.root;
int found = node;
while (node != NIL) {
found = node;
if (k < this.key[node]) {
node = this.left[node];
} else if (k == this.key[node]) {
// In the presence of duplicates, we have to check if there are
// identical
// keys to the left of us.
while ((this.left[node] != NIL) && (this.key[this.left[node]] == this.key[node])) {
node = this.left[node];
}
return node;
} else {
node = this.right[node];
}
}
// node == NIL
return found;
}
/**
* Find the node such that key[node] >= k and key[previous(node)] < k.
*/
public int findInsertionPointNoDups(int k) {
int node = this.root;
int found = node;
while (node != NIL) {
found = node;
if (k < this.key[node]) {
node = this.left[node];
} else if (k == this.key[node]) {
return node;
} else {
node = this.right[node];
}
}
// node == NIL
return found;
}
public final boolean containsKey(int k) {
return (findKey(k) != NIL);
}
private final boolean isLeftDtr(int node) {
return ((node != this.root) && (node == this.left[this.parent[node]]));
}
// private final boolean isRightDtr(int node) {
// return ((node != root) && (node == right[parent[node]]));
// }
private final int getFirstNode() {
if (this.root == NIL) {
return NIL;
}
int node = this.root;
while (this.left[node] != NIL) {
node = this.left[node];
}
return node;
}
// private final int nextNode(int node) {
// if (right[node] != NIL) {
// node = right[node];
// while (left[node] != NIL) {
// node = left[node];
// }
// } else {
// while (isRightDtr(node)) {
// node = parent[node];
// }
// if (node == root) {
// return NIL;
// }
// // node is now a left dtr, so we can go one up.
// node = parent[node];
// }
// return node;
// }
protected final int nextNode(int node) {
int y;
if (this.right[node] != NIL) {
node = this.right[node];
while (this.left[node] != NIL) {
node = this.left[node];
}
} else {
y = this.parent[node];
while ((y != NIL) && (node == this.right[y])) {
node = y;
y = this.parent[y];
}
node = y;
}
return node;
}
private final int previousNode(int node) {
if (this.left[node] != NIL) {
node = this.left[node];
while (this.right[node] != NIL) {
node = this.right[node];
}
} else {
while (isLeftDtr(node)) {
node = this.parent[node];
}
if (node == this.root) {
return NIL;
}
// node is now a left dtr, so we can go one up.
node = this.parent[node];
}
return node;
}
public boolean deleteKey(int aKey) {
int node = findKey(aKey);
if (node == NIL) {
return false;
}
deleteNode(node);
--this.size;
return true;
}
private void deleteNode(int z) {
int x, y;
if ((this.left[z] == NIL) || (this.right[z] == NIL)) {
y = z;
} else {
y = nextNode(z);
}
if (this.left[y] != NIL) {
x = this.left[y];
} else {
x = this.right[y];
}
this.parent[x] = this.parent[y];
if (this.parent[y] == NIL) {
setAsRoot(x);
} else {
if (y == this.left[this.parent[y]]) {
this.left[this.parent[y]] = x;
} else {
this.right[this.parent[y]] = x;
}
}
if (y != z) {
this.key[z] = this.key[y];
}
if (this.color[y] == black) {
deleteFixup(x);
}
}
private void deleteFixup(int x) {
int w;
while ((x != this.root) && (this.color[x] == black)) {
if (x == this.left[this.parent[x]]) {
w = this.right[this.parent[x]];
if (this.color[w] == red) {
this.color[w] = black;
this.color[this.parent[x]] = red;
leftRotate(this.parent[x]);
w = this.right[this.parent[x]];
}
if ((this.color[this.left[w]] == black) && (this.color[this.right[w]] == black)) {
this.color[w] = red;
x = this.parent[x];
} else {
if (this.color[this.right[w]] == black) {
this.color[this.left[w]] = black;
this.color[w] = red;
rightRotate(w);
w = this.right[this.parent[x]];
}
this.color[w] = this.color[this.parent[x]];
this.color[this.parent[x]] = black;
this.color[this.right[w]] = black;
leftRotate(this.parent[x]);
x = this.root;
}
} else {
w = this.left[this.parent[x]];
if (this.color[w] == red) {
this.color[w] = black;
this.color[this.parent[x]] = red;
rightRotate(this.parent[x]);
w = this.left[this.parent[x]];
}
if ((this.color[this.left[w]] == black) && (this.color[this.right[w]] == black)) {
this.color[w] = red;
x = this.parent[x];
} else {
if (this.color[this.left[w]] == black) {
this.color[this.right[w]] = black;
this.color[w] = red;
leftRotate(w);
w = this.left[this.parent[x]];
}
this.color[w] = this.color[this.parent[x]];
this.color[this.parent[x]] = black;
this.color[this.left[w]] = black;
rightRotate(this.parent[x]);
x = this.root;
}
}
}
this.color[x] = black;
}
/**
* Method iterator.
*
* @return IntListIterator
*/
public ComparableIntIterator iterator(IntComparator comp) {
return new ComparableIterator(comp);
}
public IntListIterator iterator() {
return new IntArrayRBTKeyIterator();
}
public IntPointerIterator pointerIterator() {
return new PointerIterator();
}
public IntPointerIterator pointerIterator(int aKey) {
PointerIterator it = new PointerIterator();
it.currentNode = this.findKey(aKey);
return it;
}
public ComparableIntPointerIterator pointerIterator(IntComparator comp,
int[] detectIllegalIndexUpdates, int typeCode) {
// assert(comp != null);
ComparablePointerIterator cpi = new ComparablePointerIterator(comp);
cpi.modificationSnapshot = detectIllegalIndexUpdates[typeCode];
cpi.detectIllegalIndexUpdates = detectIllegalIndexUpdates;
cpi.typeCode = typeCode;
return cpi;
}
// ///////////////////////////////////////////////////////////////////////////
// Debug utilities
public boolean satisfiesRedBlackProperties() {
// Compute depth of black nodes.
int node = this.root;
int blackDepth = 0;
while (node != NIL) {
if (this.color[node] == black) {
++blackDepth;
}
node = this.left[node];
}
return satisfiesRBProps(this.root, blackDepth, 0);
}
private boolean satisfiesRBProps(int node, final int blackDepth, int currentBlack) {
if (node == NIL) {
return (currentBlack == blackDepth);
}
if (this.color[node] == red) {
if (this.color[this.left[node]] == red || this.color[this.right[node]] == red) {
return false;
}
} else {
++currentBlack;
}
return (satisfiesRBProps(this.left[node], blackDepth, currentBlack) && satisfiesRBProps(
this.right[node], blackDepth, currentBlack));
}
public int maxDepth() {
return maxDepth(this.root, 0);
}
public int minDepth() {
return minDepth(this.root, 0);
}
public int nodeDepth(int k) {
return nodeDepth(this.root, 1, k);
}
private int nodeDepth(int node, int depth, int k) {
if (node == NIL) {
return -1;
}
if (k == this.key[node]) {
return depth;
} else if (k < this.key[node]) {
return nodeDepth(this.left[node], depth + 1, k);
} else {
return nodeDepth(this.right[node], depth + 1, k);
}
}
private int maxDepth(int node, int depth) {
if (node == NIL) {
return depth;
}
int depth1 = maxDepth(this.left[node], depth + 1);
int depth2 = maxDepth(this.right[node], depth + 1);
return (depth1 > depth2) ? depth1 : depth2;
}
private int minDepth(int node, int depth) {
if (node == NIL) {
return depth;
}
int depth1 = maxDepth(this.left[node], depth + 1);
int depth2 = maxDepth(this.right[node], depth + 1);
return (depth1 > depth2) ? depth2 : depth1;
}
public final void printKeys() {
if (this.size() == 0) {
System.out.println("Tree is empty.");
return;
}
StringBuffer buf = new StringBuffer();
printKeys(this.root, 0, buf);
System.out.println(buf);
}
private final void printKeys(int node, int offset, StringBuffer buf) {
if (node == NIL) {
// StringUtils.printSpaces(offset, buf);
// buf.append("NIL\n");
return;
}
StringUtils.printSpaces(offset, buf);
buf.append(Integer.toString(this.key[node]));
if (this.color[node] == black) {
buf.append(" BLACK");
}
buf.append("\n");
printKeys(this.left[node], offset + 2, buf);
printKeys(this.right[node], offset + 2, buf);
}
public static void main(String[] args) {
System.out.println("Constructing tree.");
IntArrayRBT tree = new IntArrayRBT();
tree.insertKeyWithDups(2);
tree.insertKeyWithDups(1);
// assert(tree.color[0] == black);
// assert(tree.size() == 0);
// assert(tree.insertKey(5) == 1);
// assert(tree.size() == 1);
// assert(tree.insertKeyWithDups(5) == 2);
// assert(tree.insertKey(3) == 3);
// assert(tree.size() == 3);
// assert(tree.insertKey(4) == 4);
// assert(tree.size() == 4);
// assert(tree.insertKey(2) == 5);
// assert(tree.size() == 5);
// tree.printKeys();
// System.out.println("Constructing tree.");
// tree = new IntArrayRBT();
// int max = 10;
// for (int i = 1; i <= max; i++) {
// tree.insertKeyWithDups(i);
// }
// for (int i = 1; i <= max; i++) {
// tree.insertKeyWithDups(i);
// }
// tree.printKeys();
// tree = new IntArrayRBT();
// max = 100;
// // System.out.println("Creating tree.");
// for (int i = 1; i <= max; i++) {
// tree.insertKeyWithDups(1);
// }
// // System.out.println("Printing tree.");
// tree.printKeys();
// // IntIterator it = tree.iterator();
// // int numElements = 0;
// // while (it.hasNext()) {
// // it.next();
// // ++numElements;
// // }
}
}