drv.RBTreeSet.drv Maven / Gradle / Ivy
Show all versions of fastutil-core Show documentation
/*
* Copyright (C) 2002-2024 Sebastiano Vigna
*
* 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 PACKAGE;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.NoSuchElementException;
/** A type-specific red-black tree set with a fast, small-footprint implementation.
*
* The iterators provided by this class are type-specific {@link
* it.unimi.dsi.fastutil.BidirectionalIterator bidirectional iterators}.
* Moreover, the iterator returned by {@code iterator()} can be safely cast
* to a type-specific {@linkplain java.util.ListIterator list iterator}.
*/
public class RB_TREE_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC implements java.io.Serializable, Cloneable, SORTED_SET KEY_GENERIC {
/** A reference to the root entry. */
protected transient Entry KEY_GENERIC tree;
/** Number of elements in this set. */
protected int count;
/** The entry of the first element of this set. */
protected transient Entry KEY_GENERIC firstEntry;
/** The entry of the last element of this set. */
protected transient Entry KEY_GENERIC lastEntry;
/** This set's comparator, as provided in the constructor. */
protected Comparator super KEY_GENERIC_CLASS> storedComparator;
/** This set's actual comparator; it may differ from {@link #storedComparator} because it is
always a type-specific comparator, so it could be derived from the former by wrapping. */
protected transient KEY_COMPARATOR KEY_SUPER_GENERIC actualComparator;
private static final long serialVersionUID = -7046029254386353130L;
{
allocatePaths();
}
/** Creates a new empty tree set.
*/
public RB_TREE_SET() {
tree = null;
count = 0;
}
/** Generates the comparator that will be actually used.
*
*
When a given {@link Comparator} is specified and stored in {@link
* #storedComparator}, we must check whether it is type-specific. If it is
* so, we can used directly, and we store it in {@link #actualComparator}. Otherwise,
* we adapt it using a helper static method.
*/
private void setActualComparator() {
#if KEY_CLASS_Object
actualComparator = storedComparator;
#else
actualComparator = COMPARATORS.AS_KEY_COMPARATOR(storedComparator);
#endif
}
/** Creates a new empty tree set with the given comparator.
*
* @param c a {@link Comparator} (even better, a type-specific comparator).
*/
public RB_TREE_SET(final Comparator super KEY_GENERIC_CLASS> c) {
this();
storedComparator = c;
setActualComparator();
}
/** Creates a new tree set copying a given collection.
*
* @param c a collection to be copied into the new tree set.
*/
public RB_TREE_SET(final Collection extends KEY_GENERIC_CLASS> c) {
this();
addAll(c);
}
/** Creates a new tree set copying a given sorted set (and its {@link Comparator}).
*
* @param s a {@link SortedSet} to be copied into the new tree set.
*/
public RB_TREE_SET(final SortedSet s) {
this(s.comparator());
addAll(s);
}
/** Creates a new tree set copying a given type-specific collection.
*
* @param c a type-specific collection to be copied into the new tree set.
*/
public RB_TREE_SET(final COLLECTION KEY_EXTENDS_GENERIC c) {
this();
addAll(c);
}
/** Creates a new tree set copying a given type-specific sorted set (and its {@link Comparator}).
*
* @param s a type-specific sorted set to be copied into the new tree set.
*/
public RB_TREE_SET(final SORTED_SET KEY_GENERIC s) {
this(s.comparator());
addAll(s);
}
/** Creates a new tree set using elements provided by a type-specific iterator.
*
* @param i a type-specific iterator whose elements will fill the set.
*/
public RB_TREE_SET(final STD_KEY_ITERATOR KEY_EXTENDS_GENERIC i) {
while(i.hasNext()) add(i.NEXT_KEY());
}
#if KEYS_PRIMITIVE
/** Creates a new tree set using elements provided by an iterator.
*
* @param i an iterator whose elements will fill the set.
*/
SUPPRESS_WARNINGS_KEY_UNCHECKED
public RB_TREE_SET(final Iterator> i) {
this(ITERATORS.AS_KEY_ITERATOR(i));
}
#endif
/** Creates a new tree set and fills it with the elements of a given array using a given {@link Comparator}.
*
* @param a an array whose elements will be used to fill the set.
* @param offset the first element to use.
* @param length the number of elements to use.
* @param c a {@link Comparator} (even better, a type-specific comparator).
*/
public RB_TREE_SET(final KEY_GENERIC_TYPE[] a, final int offset, final int length, final Comparator super KEY_GENERIC_CLASS> c) {
this(c);
ARRAYS.ensureOffsetLength(a, offset, length);
for(int i = 0; i < length; i++) add(a[offset + i]);
}
/** Creates a new tree set and fills it with the elements of a given array.
*
* @param a an array whose elements will be used to fill the set.
* @param offset the first element to use.
* @param length the number of elements to use.
*/
public RB_TREE_SET(final KEY_GENERIC_TYPE[] a, final int offset, final int length) {
this(a, offset, length, null);
}
/** Creates a new tree set copying the elements of an array.
*
* @param a an array to be copied into the new tree set.
*/
public RB_TREE_SET(final KEY_GENERIC_TYPE[] a) {
this();
int i = a.length;
while(i-- != 0) add(a[i]);
}
/** Creates a new tree set copying the elements of an array using a given {@link Comparator}.
*
* @param a an array to be copied into the new tree set.
* @param c a {@link Comparator} (even better, a type-specific comparator).
*/
public RB_TREE_SET(final KEY_GENERIC_TYPE[] a, final Comparator super KEY_GENERIC_CLASS> c) {
this(c);
int i = a.length;
while(i-- != 0) add(a[i]);
}
/*
* The following methods implements some basic building blocks used by
* all accessors. They are (and should be maintained) identical to those used in RBTreeMap.drv.
*
* The add()/remove() code is derived from Ben Pfaff's GNU libavl
* (https://adtinfo.org/). If you want to understand what's
* going on, you should have a look at the literate code contained therein
* first.
*/
/** Compares two keys in the right way.
*
* This method uses the {@link #actualComparator} if it is non-{@code null}.
* Otherwise, it resorts to primitive type comparisons or to {@link Comparable#compareTo(Object) compareTo()}.
*
* @param k1 the first key.
* @param k2 the second key.
* @return a number smaller than, equal to or greater than 0, as usual
* (i.e., when k1 < k2, k1 = k2 or k1 > k2, respectively).
*/
SUPPRESS_WARNINGS_KEY_UNCHECKED
final int compare(final KEY_GENERIC_TYPE k1, final KEY_GENERIC_TYPE k2) {
return actualComparator == null ? KEY_CMP(k1, k2) : actualComparator.compare(k1, k2);
}
/** Returns the entry corresponding to the given key, if it is in the tree; {@code null}, otherwise.
*
* @param k the key to search for.
* @return the corresponding entry, or {@code null} if no entry with the given key exists.
*/
private Entry KEY_GENERIC findKey(final KEY_GENERIC_TYPE k) {
Entry KEY_GENERIC e = tree;
int cmp;
while (e != null && (cmp = compare(k, e.key)) != 0)
e = cmp < 0 ? e.left() : e.right();
return e;
}
/** Locates a key.
*
* @param k a key.
* @return the last entry on a search for the given key; this will be
* the given key, if it present; otherwise, it will be either the smallest greater key or the greatest smaller key.
*/
final Entry KEY_GENERIC locateKey(final KEY_GENERIC_TYPE k) {
Entry KEY_GENERIC e = tree, last = tree;
int cmp = 0;
while (e != null && (cmp = compare(k, e.key)) != 0) {
last = e;
e = cmp < 0 ? e.left() : e.right();
}
return cmp == 0 ? e : last;
}
/** This vector remembers the path and the direction followed during the
* current insertion. It suffices for about 232 entries. */
private transient boolean dirPath[];
private transient Entry KEY_GENERIC nodePath[];
SUPPRESS_WARNINGS_KEY_UNCHECKED_RAWTYPES
private void allocatePaths() {
dirPath = new boolean[64];
nodePath = new Entry[64];
}
@Override
public boolean add(final KEY_GENERIC_TYPE k) {
REQUIRE_KEY_NON_NULL(k)
int maxDepth = 0;
if (tree == null) { // The case of the empty tree is treated separately.
count++;
tree = lastEntry = firstEntry = new Entry KEY_GENERIC_DIAMOND(k);
}
else {
Entry KEY_GENERIC p = tree, e;
int cmp, i = 0;
while(true) {
if ((cmp = compare(k, p.key)) == 0) {
// We clean up the node path, or we could have stale references later.
while(i-- != 0) nodePath[i] = null;
return false;
}
nodePath[i] = p;
if (dirPath[i++] = cmp > 0) {
if (p.succ()) {
count++;
e = new Entry KEY_GENERIC_DIAMOND(k);
if (p.right == null) lastEntry = e;
e.left = p;
e.right = p.right;
p.right(e);
break;
}
p = p.right;
}
else {
if (p.pred()) {
count++;
e = new Entry KEY_GENERIC_DIAMOND(k);
if (p.left == null) firstEntry = e;
e.right = p;
e.left = p.left;
p.left(e);
break;
}
p = p.left;
}
}
maxDepth = i--;
while(i > 0 && ! nodePath[i].black()) {
if (! dirPath[i - 1]) {
Entry KEY_GENERIC y = nodePath[i - 1].right;
if (! nodePath[i - 1].succ() && ! y.black()) {
nodePath[i].black(true);
y.black(true);
nodePath[i - 1].black(false);
i -= 2;
}
else {
Entry KEY_GENERIC x;
if (! dirPath[i]) y = nodePath[i];
else {
x = nodePath[i];
y = x.right;
x.right = y.left;
y.left = x;
nodePath[i - 1].left = y;
if (y.pred()) {
y.pred(false);
x.succ(y);
}
}
x = nodePath[i - 1];
x.black(false);
y.black(true);
x.left = y.right;
y.right = x;
if (i < 2) tree = y;
else {
if (dirPath[i - 2]) nodePath[i - 2].right = y;
else nodePath[i - 2].left = y;
}
if (y.succ()) {
y.succ(false);
x.pred(y);
}
break;
}
}
else {
Entry KEY_GENERIC y = nodePath[i - 1].left;
if (! nodePath[i - 1].pred() && ! y.black()) {
nodePath[i].black(true);
y.black(true);
nodePath[i - 1].black(false);
i -= 2;
}
else {
Entry KEY_GENERIC x;
if (dirPath[i]) y = nodePath[i];
else {
x = nodePath[i];
y = x.left;
x.left = y.right;
y.right = x;
nodePath[i - 1].right = y;
if (y.succ()) {
y.succ(false);
x.pred(y);
}
}
x = nodePath[i - 1];
x.black(false);
y.black(true);
x.right = y.left;
y.left = x;
if (i < 2) tree = y;
else {
if (dirPath[i - 2]) nodePath[i - 2].right = y;
else nodePath[i - 2].left = y;
}
if (y.pred()){
y.pred(false);
x.succ(y);
}
break;
}
}
}
}
tree.black(true);
// We clean up the node path, or we could have stale references later.
while(maxDepth-- != 0) nodePath[maxDepth] = null;
return true;
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
@Override
public boolean remove(final KEY_TYPE k) {
if (tree == null) return false;
Entry KEY_GENERIC p = tree;
int cmp;
int i = 0;
final KEY_GENERIC_TYPE kk = KEY_GENERIC_CAST k;
while(true) {
if ((cmp = compare(kk, p.key)) == 0) break;
dirPath[i] = cmp > 0;
nodePath[i] = p;
if (dirPath[i++]) {
if ((p = p.right()) == null) {
// We clean up the node path, or we could have stale references later.
while(i-- != 0) nodePath[i] = null;
return false;
}
}
else {
if ((p = p.left()) == null) {
// We clean up the node path, or we could have stale references later.
while(i-- != 0) nodePath[i] = null;
return false;
}
}
}
if (p.left == null) firstEntry = p.next();
if (p.right == null) lastEntry = p.prev();
if (p.succ()) {
if (p.pred()) {
if (i == 0) tree = p.left;
else {
if (dirPath[i - 1]) nodePath[i - 1].succ(p.right);
else nodePath[i - 1].pred(p.left);
}
}
else {
p.prev().right = p.right;
if (i == 0) tree = p.left;
else {
if (dirPath[i - 1]) nodePath[i - 1].right = p.left;
else nodePath[i - 1].left = p.left;
}
}
}
else {
boolean color;
Entry KEY_GENERIC r = p.right;
if (r.pred()) {
r.left = p.left;
r.pred(p.pred());
if (! r.pred()) r.prev().right = r;
if (i == 0) tree = r;
else {
if (dirPath[i - 1]) nodePath[i - 1].right = r;
else nodePath[i - 1].left = r;
}
color = r.black();
r.black(p.black());
p.black(color);
dirPath[i] = true;
nodePath[i++] = r;
}
else {
Entry KEY_GENERIC s;
int j = i++;
while(true) {
dirPath[i] = false;
nodePath[i++] = r;
s = r.left;
if (s.pred()) break;
r = s;
}
dirPath[j] = true;
nodePath[j] = s;
if (s.succ()) r.pred(s);
else r.left = s.right;
s.left = p.left;
if (! p.pred()) {
p.prev().right = s;
s.pred(false);
}
s.right(p.right);
color = s.black();
s.black(p.black());
p.black(color);
if (j == 0) tree = s;
else {
if (dirPath[j - 1]) nodePath[j - 1].right = s;
else nodePath[j - 1].left = s;
}
}
}
int maxDepth = i;
if (p.black()) {
for(; i > 0; i--) {
if (dirPath[i - 1] && ! nodePath[i - 1].succ() ||
! dirPath[i - 1] && ! nodePath[i - 1].pred()) {
Entry KEY_GENERIC x = dirPath[i - 1] ? nodePath[i - 1].right : nodePath[i - 1].left;
if (! x.black()) {
x.black(true);
break;
}
}
if (! dirPath[i - 1]) {
Entry KEY_GENERIC w = nodePath[i - 1].right;
if (! w.black()) {
w.black(true);
nodePath[i - 1].black(false);
nodePath[i - 1].right = w.left;
w.left = nodePath[i - 1];
if (i < 2) tree = w;
else {
if (dirPath[i - 2]) nodePath[i - 2].right = w;
else nodePath[i - 2].left = w;
}
nodePath[i] = nodePath[i - 1];
dirPath[i] = false;
nodePath[i - 1] = w;
if (maxDepth == i++) maxDepth++;
w = nodePath[i - 1].right;
}
if ((w.pred() || w.left.black()) &&
(w.succ() || w.right.black())) {
w.black(false);
}
else {
if (w.succ() || w.right.black()) {
Entry KEY_GENERIC y = w.left;
y.black (true);
w.black(false);
w.left = y.right;
y.right = w;
w = nodePath[i - 1].right = y;
if (w.succ()) {
w.succ(false);
w.right.pred(w);
}
}
w.black(nodePath[i - 1].black());
nodePath[i - 1].black(true);
w.right.black(true);
nodePath[i - 1].right = w.left;
w.left = nodePath[i - 1];
if (i < 2) tree = w;
else {
if (dirPath[i - 2]) nodePath[i - 2].right = w;
else nodePath[i - 2].left = w;
}
if (w.pred()) {
w.pred(false);
nodePath[i - 1].succ(w);
}
break;
}
}
else {
Entry KEY_GENERIC w = nodePath[i - 1].left;
if (! w.black()) {
w.black (true);
nodePath[i - 1].black(false);
nodePath[i - 1].left = w.right;
w.right = nodePath[i - 1];
if (i < 2) tree = w;
else {
if (dirPath[i - 2]) nodePath[i - 2].right = w;
else nodePath[i - 2].left = w;
}
nodePath[i] = nodePath[i - 1];
dirPath[i] = true;
nodePath[i - 1] = w;
if (maxDepth == i++) maxDepth++;
w = nodePath[i - 1].left;
}
if ((w.pred() || w.left.black()) &&
(w.succ() || w.right.black())) {
w.black(false);
}
else {
if (w.pred() || w.left.black()) {
Entry KEY_GENERIC y = w.right;
y.black(true);
w.black (false);
w.right = y.left;
y.left = w;
w = nodePath[i - 1].left = y;
if (w.pred()) {
w.pred(false);
w.left.succ(w);
}
}
w.black(nodePath[i - 1].black());
nodePath[i - 1].black(true);
w.left.black(true);
nodePath[i - 1].left = w.right;
w.right = nodePath[i - 1];
if (i < 2) tree = w;
else {
if (dirPath[i - 2]) nodePath[i - 2].right = w;
else nodePath[i - 2].left = w;
}
if (w.succ()) {
w.succ(false);
nodePath[i - 1].pred(w);
}
break;
}
}
}
if (tree != null) tree.black(true);
}
count--;
// We clean up the node path, or we could have stale references later.
while(maxDepth-- != 0) nodePath[maxDepth] = null;
return true;
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
@Override
public boolean contains(final KEY_TYPE k) {
return findKey(KEY_GENERIC_CAST k) != null;
}
#if KEY_CLASS_Object
SUPPRESS_WARNINGS_KEY_UNCHECKED
public K get(final KEY_TYPE k) {
final Entry KEY_GENERIC entry = findKey(KEY_GENERIC_CAST k);
return entry == null ? null : entry.key;
}
#endif
@Override
public void clear() {
count = 0;
tree = null;
firstEntry = lastEntry = null;
}
/** This class represent an entry in a tree set.
*
*
We use the only "metadata", i.e., {@link Entry#info}, to store
* information about color, predecessor status and successor status.
*
*
Note that since the class is recursive, it can be
* considered equivalently a tree.
*/
private static final class Entry KEY_GENERIC implements Cloneable {
/** The the bit in this mask is true, the node is black. */
private static final int BLACK_MASK = 1;
/** If the bit in this mask is true, {@link #right} points to a successor. */
private static final int SUCC_MASK = 1 << 31;
/** If the bit in this mask is true, {@link #left} points to a predecessor. */
private static final int PRED_MASK = 1 << 30;
/** The key of this entry. */
KEY_GENERIC_TYPE key;
/** The pointers to the left and right subtrees. */
Entry KEY_GENERIC left, right;
/** This integers holds different information in different bits (see {@link #SUCC_MASK}, {@link #PRED_MASK} and {@link #BLACK_MASK}). */
int info;
Entry() {}
/** Creates a new red entry with the given key.
*
* @param k a key.
*/
Entry(final KEY_GENERIC_TYPE k) {
this.key = k;
info = SUCC_MASK | PRED_MASK;
}
/** Returns the left subtree.
*
* @return the left subtree ({@code null} if the left
* subtree is empty).
*/
Entry KEY_GENERIC left() {
return (info & PRED_MASK) != 0 ? null : left;
}
/** Returns the right subtree.
*
* @return the right subtree ({@code null} if the right
* subtree is empty).
*/
Entry KEY_GENERIC right() {
return (info & SUCC_MASK) != 0 ? null : right;
}
/** Checks whether the left pointer is really a predecessor.
* @return true if the left pointer is a predecessor.
*/
boolean pred() {
return (info & PRED_MASK) != 0;
}
/** Checks whether the right pointer is really a successor.
* @return true if the right pointer is a successor.
*/
boolean succ() {
return (info & SUCC_MASK) != 0;
}
/** Sets whether the left pointer is really a predecessor.
* @param pred if true then the left pointer will be considered a predecessor.
*/
void pred(final boolean pred) {
if (pred) info |= PRED_MASK;
else info &= ~PRED_MASK;
}
/** Sets whether the right pointer is really a successor.
* @param succ if true then the right pointer will be considered a successor.
*/
void succ(final boolean succ) {
if (succ) info |= SUCC_MASK;
else info &= ~SUCC_MASK;
}
/** Sets the left pointer to a predecessor.
* @param pred the predecessr.
*/
void pred(final Entry KEY_GENERIC pred) {
info |= PRED_MASK;
left = pred;
}
/** Sets the right pointer to a successor.
* @param succ the successor.
*/
void succ(final Entry KEY_GENERIC succ) {
info |= SUCC_MASK;
right = succ;
}
/** Sets the left pointer to the given subtree.
* @param left the new left subtree.
*/
void left(final Entry KEY_GENERIC left) {
info &= ~PRED_MASK;
this.left = left;
}
/** Sets the right pointer to the given subtree.
* @param right the new right subtree.
*/
void right(final Entry KEY_GENERIC right) {
info &= ~SUCC_MASK;
this.right = right;
}
/** Returns whether this node is black.
* @return true iff this node is black.
*/
boolean black() {
return (info & BLACK_MASK) != 0;
}
/** Sets whether this node is black.
* @param black if true, then this node becomes black; otherwise, it becomes red..
*/
void black(final boolean black) {
if (black) info |= BLACK_MASK;
else info &= ~BLACK_MASK;
}
/** Computes the next entry in the set order.
*
* @return the next entry ({@code null}) if this is the last entry).
*/
Entry KEY_GENERIC next() {
Entry KEY_GENERIC next = this.right;
if ((info & SUCC_MASK) == 0) while ((next.info & PRED_MASK) == 0) next = next.left;
return next;
}
/** Computes the previous entry in the set order.
*
* @return the previous entry ({@code null}) if this is the first entry).
*/
Entry KEY_GENERIC prev() {
Entry KEY_GENERIC prev = this.left;
if ((info & PRED_MASK) == 0) while ((prev.info & SUCC_MASK) == 0) prev = prev.right;
return prev;
}
@Override
SUPPRESS_WARNINGS_KEY_UNCHECKED
public Entry KEY_GENERIC clone() {
Entry KEY_GENERIC c;
try {
c = (Entry KEY_GENERIC)super.clone();
}
catch(CloneNotSupportedException cantHappen) {
throw new InternalError();
}
c.key = key;
c.info = info;
return c;
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof Entry)) return false;
Entry KEY_GENERIC_WILDCARD e = (Entry KEY_GENERIC_WILDCARD)o;
return KEY_EQUALS(key, e.key);
}
@Override
public int hashCode() {
return KEY2JAVAHASH_NOT_NULL(key);
}
@Override
public String toString() {
return String.valueOf(key);
}
/*
public void prettyPrint() {
prettyPrint(0);
}
public void prettyPrint(int level) {
if (pred()) {
for (int i = 0; i < level; i++)
System.err.print(" ");
System.err.println("pred: " + left);
}
else if (left != null)
left.prettyPrint(level +1);
for (int i = 0; i < level; i++)
System.err.print(" ");
System.err.println(key + " (" + (black() ? "black" : "red") + ")");
if (succ()) {
for (int i = 0; i < level; i++)
System.err.print(" ");
System.err.println("succ: " + right);
}
else if (right != null)
right.prettyPrint(level + 1);
}*/
}
/*
public void prettyPrint() {
System.err.println("size: " + count);
if (tree != null) tree.prettyPrint();
}
*/
@Override
public int size() {
return count;
}
@Override
public boolean isEmpty() {
return count == 0;
}
@Override
public KEY_GENERIC_TYPE FIRST() {
if (tree == null) throw new NoSuchElementException();
return firstEntry.key;
}
@Override
public KEY_GENERIC_TYPE LAST() {
if (tree == null) throw new NoSuchElementException();
return lastEntry.key;
}
/** An iterator on the whole range.
*
*
This class can iterate in both directions on a threaded tree.
*/
private class SetIterator implements KEY_LIST_ITERATOR KEY_GENERIC {
/** The entry that will be returned by the next call to {@link java.util.ListIterator#previous()} (or {@code null} if no previous entry exists). */
Entry KEY_GENERIC prev;
/** The entry that will be returned by the next call to {@link java.util.ListIterator#next()} (or {@code null} if no next entry exists). */
Entry KEY_GENERIC next;
/** The last entry that was returned (or {@code null} if we did not iterate or used {@link #remove()}). */
Entry KEY_GENERIC curr;
/** The current index (in the sense of a {@link java.util.ListIterator}). Note that this value is not meaningful when this iterator has been created using the nonempty constructor.*/
int index = 0;
SetIterator() {
next = firstEntry;
}
SetIterator(final KEY_GENERIC_TYPE k) {
if ((next = locateKey(k)) != null) {
if (compare(next.key, k) <= 0) {
prev = next;
next = next.next();
}
else prev = next.prev();
}
}
@Override
public boolean hasNext() { return next != null; }
@Override
public boolean hasPrevious() { return prev != null; }
void updateNext() { next = next.next(); }
void updatePrevious() { prev = prev.prev(); }
@Override
public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; }
@Override
public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; }
Entry KEY_GENERIC nextEntry() {
if (! hasNext()) throw new NoSuchElementException();
curr = prev = next;
index++;
updateNext();
return curr;
}
Entry KEY_GENERIC previousEntry() {
if (! hasPrevious()) throw new NoSuchElementException();
curr = next = prev;
index--;
updatePrevious();
return curr;
}
@Override
public int nextIndex() { return index; }
@Override
public int previousIndex() { return index - 1; }
@Override
public void remove() {
if (curr == null) throw new IllegalStateException();
/* If the last operation was a next(), we are removing an entry that preceeds
the current index, and thus we must decrement it. */
if (curr == prev) index--;
next = prev = curr;
updatePrevious();
updateNext();
RB_TREE_SET.this.remove(curr.key);
curr = null;
}
}
@Override
public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SetIterator(); }
@Override
public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) { return new SetIterator(from); }
@Override
public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() { return actualComparator; }
@Override
public SORTED_SET KEY_GENERIC headSet(final KEY_GENERIC_TYPE to) { return new Subset(KEY_NULL, true, to, false); }
@Override
public SORTED_SET KEY_GENERIC tailSet(final KEY_GENERIC_TYPE from) { return new Subset(from, false, KEY_NULL, true); }
@Override
public SORTED_SET KEY_GENERIC subSet(final KEY_GENERIC_TYPE from, final KEY_GENERIC_TYPE to) { return new Subset(from, false, to, false); }
/** A subset with given range.
*
*
This class represents a subset. One has to specify the left/right
* limits (which can be set to -∞ or ∞). Since the subset is a
* view on the set, at a given moment it could happen that the limits of
* the range are not any longer in the main set. Thus, things such as
* {@link java.util.SortedSet#first()} or {@link java.util.Collection#size()} must be always computed
* on-the-fly.
*/
private final class Subset extends ABSTRACT_SORTED_SET KEY_GENERIC implements java.io.Serializable, SORTED_SET KEY_GENERIC {
private static final long serialVersionUID = -7046029254386353129L;
/** The start of the subset range, unless {@link #bottom} is true. */
KEY_GENERIC_TYPE from;
/** The end of the subset range, unless {@link #top} is true. */
KEY_GENERIC_TYPE to;
/** If true, the subset range starts from -∞. */
boolean bottom;
/** If true, the subset range goes to ∞. */
boolean top;
/** Creates a new subset with given key range.
*
* @param from the start of the subset range.
* @param bottom if true, the first parameter is ignored and the range starts from -∞.
* @param to the end of the subset range.
* @param top if true, the third parameter is ignored and the range goes to ∞.
*/
public Subset(final KEY_GENERIC_TYPE from, final boolean bottom, final KEY_GENERIC_TYPE to, final boolean top) {
if (! bottom && ! top && RB_TREE_SET.this.compare(from, to) > 0) throw new IllegalArgumentException("Start element (" + from + ") is larger than end element (" + to + ")");
this.from = from;
this.bottom = bottom;
this.to = to;
this.top = top;
}
@Override
public void clear() {
final SubsetIterator i = new SubsetIterator();
while(i.hasNext()) {
i.NEXT_KEY();
i.remove();
}
}
/** Checks whether a key is in the subset range.
* @param k a key.
* @return true if is the key is in the subset range.
*/
final boolean in(final KEY_GENERIC_TYPE k) {
return (bottom || RB_TREE_SET.this.compare(k, from) >= 0) &&
(top || RB_TREE_SET.this.compare(k, to) < 0);
}
@Override
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean contains(final KEY_TYPE k) {
return in(KEY_GENERIC_CAST k) && RB_TREE_SET.this.contains(k);
}
@Override
public boolean add(final KEY_GENERIC_TYPE k) {
if (! in(k)) throw new IllegalArgumentException("Element (" + k + ") out of range [" + (bottom ? "-" : String.valueOf(from)) + ", " + (top ? "-" : String.valueOf(to)) + ")");
return RB_TREE_SET.this.add(k);
}
@Override
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean remove(final KEY_TYPE k) {
if (! in(KEY_GENERIC_CAST k)) return false;
return RB_TREE_SET.this.remove(k);
}
@Override
public int size() {
final SubsetIterator i = new SubsetIterator();
int n = 0;
while(i.hasNext()) {
n++;
i.NEXT_KEY();
}
return n;
}
@Override
public boolean isEmpty() {
return ! new SubsetIterator().hasNext();
}
@Override
public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() {
return actualComparator;
}
@Override
public KEY_BIDI_ITERATOR KEY_GENERIC iterator() {
return new SubsetIterator();
}
@Override
public KEY_BIDI_ITERATOR KEY_GENERIC iterator(final KEY_GENERIC_TYPE from) {
return new SubsetIterator(from);
}
@Override
public SORTED_SET KEY_GENERIC headSet(final KEY_GENERIC_TYPE to) {
if (top) return new Subset(from, bottom, to, false);
return compare(to, this.to) < 0 ? new Subset(from, bottom, to, false) : this;
}
@Override
public SORTED_SET KEY_GENERIC tailSet(final KEY_GENERIC_TYPE from) {
if (bottom) return new Subset(from, false, to, top);
return compare(from, this.from) > 0 ? new Subset(from, false, to, top) : this;
}
@Override
public SORTED_SET KEY_GENERIC subSet(KEY_GENERIC_TYPE from, KEY_GENERIC_TYPE to) {
if (top && bottom) return new Subset(from, false, to, false);
if (! top) to = compare(to, this.to) < 0 ? to : this.to;
if (! bottom) from = compare(from, this.from) > 0 ? from : this.from;
if (! top && ! bottom && from == this.from && to == this.to) return this;
return new Subset(from, false, to, false);
}
/** Locates the first entry.
*
* @return the first entry of this subset, or {@code null} if the subset is empty.
*/
public RB_TREE_SET.Entry KEY_GENERIC firstEntry() {
if (tree == null) return null;
// If this subset goes to -infinity, we return the main set first entry; otherwise, we locate the start of the set.
RB_TREE_SET.Entry KEY_GENERIC e;
if (bottom) e = firstEntry;
else {
e = locateKey(from);
// If we find either the start or something greater we're OK.
if (compare(e.key, from) < 0) e = e.next();
}
// Finally, if this subset doesn't go to infinity, we check that the resulting key isn't greater than the end.
if (e == null || ! top && compare(e.key, to) >= 0) return null;
return e;
}
/** Locates the last entry.
*
* @return the last entry of this subset, or {@code null} if the subset is empty.
*/
public RB_TREE_SET.Entry KEY_GENERIC lastEntry() {
if (tree == null) return null;
// If this subset goes to infinity, we return the main set last entry; otherwise, we locate the end of the set.
RB_TREE_SET.Entry KEY_GENERIC e;
if (top) e = lastEntry;
else {
e = locateKey(to);
// If we find something smaller than the end we're OK.
if (compare(e.key, to) >= 0) e = e.prev();
}
// Finally, if this subset doesn't go to -infinity, we check that the resulting key isn't smaller than the start.
if (e == null || ! bottom && compare(e.key, from) < 0) return null;
return e;
}
@Override
public KEY_GENERIC_TYPE FIRST() {
RB_TREE_SET.Entry KEY_GENERIC e = firstEntry();
if (e == null) throw new NoSuchElementException();
return e.key;
}
@Override
public KEY_GENERIC_TYPE LAST() {
RB_TREE_SET.Entry KEY_GENERIC e = lastEntry();
if (e == null) throw new NoSuchElementException();
return e.key;
}
/** An iterator for subranges.
*
*
This class inherits from {@link SetIterator}, but overrides the methods that
* update the pointer after a {@link java.util.ListIterator#next()} or {@link java.util.ListIterator#previous()}. If we would
* move out of the range of the subset we just overwrite the next or previous
* entry with {@code null}.
*/
private final class SubsetIterator extends SetIterator {
SubsetIterator() {
next = firstEntry();
}
SubsetIterator(final KEY_GENERIC_TYPE k) {
this();
if (next != null) {
if (! bottom && compare(k, next.key) < 0) prev = null;
else if (! top && compare(k, (prev = lastEntry()).key) >= 0) next = null;
else {
next = locateKey(k);
if (compare(next.key, k) <= 0) {
prev = next;
next = next.next();
}
else prev = next.prev();
}
}
}
@Override
void updatePrevious() {
prev = prev.prev();
if (! bottom && prev != null && RB_TREE_SET.this.compare(prev.key, from) < 0) prev = null;
}
@Override
void updateNext() {
next = next.next();
if (! top && next != null && RB_TREE_SET.this.compare(next.key, to) >= 0) next = null;
}
}
}
/** Returns a deep copy of this tree set.
*
*
This method performs a deep copy of this tree set; the data stored in the
* set, however, is not cloned. Note that this makes a difference only for object keys.
*
* @return a deep copy of this tree set.
*/
@Override
SUPPRESS_WARNINGS_KEY_UNCHECKED
public Object clone() {
RB_TREE_SET KEY_GENERIC c;
try {
c = (RB_TREE_SET KEY_GENERIC)super.clone();
}
catch(CloneNotSupportedException cantHappen) {
throw new InternalError();
}
c.allocatePaths();
if (count != 0) {
// Also this apparently unfathomable code is derived from GNU libavl.
Entry KEY_GENERIC e, p, q, rp = new Entry KEY_GENERIC_DIAMOND(), rq = new Entry KEY_GENERIC_DIAMOND();
p = rp;
rp.left(tree);
q = rq;
rq.pred(null);
while(true) {
if (! p.pred()) {
e = p.left.clone();
e.pred(q.left);
e.succ(q);
q.left(e);
p = p.left;
q = q.left;
}
else {
while(p.succ()) {
p = p.right;
if (p == null) {
q.right = null;
c.tree = rq.left;
c.firstEntry = c.tree;
while(c.firstEntry.left != null) c.firstEntry = c.firstEntry.left;
c.lastEntry = c.tree;
while(c.lastEntry.right != null) c.lastEntry = c.lastEntry.right;
return c;
}
q = q.right;
}
p = p.right;
q = q.right;
}
if (! p.succ()) {
e = p.right.clone();
e.succ(q.right);
e.pred(q);
q.right(e);
}
}
}
return c;
}
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
int n = count;
SetIterator i = new SetIterator();
s.defaultWriteObject();
while(n-- != 0) s.WRITE_KEY(i.NEXT_KEY());
}
/** Reads the given number of entries from the input stream, returning the corresponding tree.
*
* @param s the input stream.
* @param n the (positive) number of entries to read.
* @param pred the entry containing the key that preceeds the first key in the tree.
* @param succ the entry containing the key that follows the last key in the tree.
*/
SUPPRESS_WARNINGS_KEY_UNCHECKED
private Entry KEY_GENERIC readTree(final java.io.ObjectInputStream s, final int n, final Entry KEY_GENERIC pred, final Entry KEY_GENERIC succ) throws java.io.IOException, ClassNotFoundException {
if (n == 1) {
final Entry KEY_GENERIC top = new Entry KEY_GENERIC_DIAMOND(KEY_GENERIC_CAST s.READ_KEY());
top.pred(pred);
top.succ(succ);
top.black(true);
return top;
}
if (n == 2) {
/* We handle separately this case so that recursion will
*always* be on nonempty subtrees. */
final Entry KEY_GENERIC top = new Entry KEY_GENERIC_DIAMOND(KEY_GENERIC_CAST s.READ_KEY());
top.black(true);
top.right(new Entry KEY_GENERIC_DIAMOND(KEY_GENERIC_CAST s.READ_KEY()));
top.right.pred(top);
top.pred(pred);
top.right.succ(succ);
return top;
}
// The right subtree is the largest one.
final int rightN = n / 2, leftN = n - rightN - 1;
final Entry KEY_GENERIC top = new Entry KEY_GENERIC_DIAMOND();
top.left(readTree(s, leftN, pred, top));
top.key = KEY_GENERIC_CAST s.READ_KEY();
top.black(true);
top.right(readTree(s, rightN, top, succ));
if (n + 2 == ((n + 2) & -(n + 2))) top.right.black(false); // Quick test for determining whether n + 2 is a power of 2.
return top;
}
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
/* The storedComparator is now correctly set, but we must restore
on-the-fly the actualComparator. */
setActualComparator();
allocatePaths();
if (count != 0) {
tree = readTree(s, count, null, null);
Entry KEY_GENERIC e;
e = tree;
while(e.left() != null) e = e.left();
firstEntry = e;
e = tree;
while(e.right() != null) e = e.right();
lastEntry = e;
}
}
#ifdef ASSERTS_CODE
private void checkNodePath() {
for(int i = nodePath.length; i-- != 0;) assert nodePath[i] == null : i;
}
private static KEY_GENERIC int checkTree(Entry KEY_GENERIC e, int d, int D) {
if (e == null) return 0;
if (e.black()) d++;
if (e.left() != null) D = checkTree(e.left(), d, D);
if (e.right() != null) D = checkTree(e.right(), d, D);
if (e.left() == null && e.right() == null) {
if (D == -1) D = d;
else if (D != d) throw new AssertionError("Mismatch between number of black nodes (" + D + " and " + d + ")");
}
return D;
}
#endif
#ifdef TEST
private static long seed = System.currentTimeMillis();
private static java.util.Random r = new java.util.Random(seed);
private static KEY_TYPE genKey() {
#if KEY_CLASS_Byte || KEY_CLASS_Short || KEY_CLASS_Character
return (KEY_TYPE)(r.nextInt());
#elif KEYS_PRIMITIVE
return r.NEXT_KEY();
#else
return Integer.toBinaryString(r.nextInt());
#endif
}
private static java.text.NumberFormat format = new java.text.DecimalFormat("#,###.00");
private static java.text.FieldPosition p = new java.text.FieldPosition(0);
private static String format(double d) {
StringBuffer s = new StringBuffer();
return format.format(d, s, p).toString();
}
private static void speedTest(int n, boolean comp) {
int i, j;
RB_TREE_SET m;
java.util.TreeSet t;
KEY_TYPE k[] = new KEY_TYPE[n];
KEY_TYPE nk[] = new KEY_TYPE[n];
long ms;
for(i = 0; i < n; i++) {
k[i] = genKey();
nk[i] = genKey();
}
double totAdd = 0, totYes = 0, totNo = 0, totIterFor = 0, totIterBack = 0, totRemYes = 0, d, dd;
if (comp) {
for(j = 0; j < 20; j++) {
t = new java.util.TreeSet();
/* We first add all pairs to t. */
for(i = 0; i < n; i++) t.add(KEY2OBJ(k[i]));
/* Then we remove the first half and put it back. */
for(i = 0; i < n/2; i++) t.remove(KEY2OBJ(k[i]));
ms = System.currentTimeMillis();
for(i = 0; i < n/2; i++) t.add(KEY2OBJ(k[i]));
d = System.currentTimeMillis() - ms;
/* Then we remove the other half and put it back again. */
ms = System.currentTimeMillis();
for(i = n/2; i < n; i++) t.remove(KEY2OBJ(k[i]));
dd = System.currentTimeMillis() - ms ;
ms = System.currentTimeMillis();
for(i = n/2; i < n; i++) t.add(KEY2OBJ(k[i]));
d += System.currentTimeMillis() - ms;
if (j > 2) totAdd += n/d;
System.out.print("Add: " + format(n/d) +" K/s ");
/* Then we remove again the first half. */
ms = System.currentTimeMillis();
for(i = 0; i < n/2; i++) t.remove(KEY2OBJ(k[i]));
dd += System.currentTimeMillis() - ms ;
if (j > 2) totRemYes += n/dd;
System.out.print("RemYes: " + format(n/dd) +" K/s ");
/* And then we put it back. */
for(i = 0; i < n/2; i++) t.add(KEY2OBJ(k[i]));
/* We check for pairs in t. */
ms = System.currentTimeMillis();
for(i = 0; i < n; i++) t.contains(KEY2OBJ(k[i]));
d = 1.0 * n / (System.currentTimeMillis() - ms);
if (j > 2) totYes += d;
System.out.print("Yes: " + format(d) +" K/s ");
/* We check for pairs not in t. */
ms = System.currentTimeMillis();
for(i = 0; i < n; i++) t.contains(KEY2OBJ(nk[i]));
d = 1.0 * n / (System.currentTimeMillis() - ms);
if (j > 2) totNo += d;
System.out.print("No: " + format(d) +" K/s ");
/* We iterate on t. */
ms = System.currentTimeMillis();
for(Iterator it = t.iterator(); it.hasNext(); it.next());
d = 1.0 * n / (System.currentTimeMillis() - ms);
if (j > 2) totIterFor += d;
System.out.print("IterFor: " + format(d) +" K/s ");
System.out.println();
}
System.out.println();
System.out.println("java.util Add: " + format(totAdd/(j-3)) + " K/s RemYes: " + format(totRemYes/(j-3)) + " K/s Yes: " + format(totYes/(j-3)) + " K/s No: " + format(totNo/(j-3)) + " K/s IterFor: " + format(totIterFor/(j-3)) + " K/s");
System.out.println();
totAdd = totYes = totNo = totIterFor = totIterBack = totRemYes = 0;
}
for(j = 0; j < 20; j++) {
m = new RB_TREE_SET();
/* We first add all pairs to m. */
for(i = 0; i < n; i++) m.add(k[i]);
/* Then we remove the first half and put it back. */
for(i = 0; i < n/2; i++) m.remove(k[i]);
ms = System.currentTimeMillis();
for(i = 0; i < n/2; i++) m.add(k[i]);
d = System.currentTimeMillis() - ms;
/* Then we remove the other half and put it back again. */
ms = System.currentTimeMillis();
for(i = n/2; i < n; i++) m.remove(k[i]);
dd = System.currentTimeMillis() - ms ;
ms = System.currentTimeMillis();
for(i = n/2; i < n; i++) m.add(k[i]);
d += System.currentTimeMillis() - ms;
if (j > 2) totAdd += n/d;
System.out.print("Add: " + format(n/d) +" K/s ");
/* Then we remove again the first half. */
ms = System.currentTimeMillis();
for(i = 0; i < n/2; i++) m.remove(k[i]);
dd += System.currentTimeMillis() - ms ;
if (j > 2) totRemYes += n/dd;
System.out.print("RemYes: " + format(n/dd) +" K/s ");
/* And then we put it back. */
for(i = 0; i < n/2; i++) m.add(k[i]);
/* We check for pairs in m. */
ms = System.currentTimeMillis();
for(i = 0; i < n; i++) m.contains(k[i]);
d = 1.0 * n / (System.currentTimeMillis() - ms);
if (j > 2) totYes += d;
System.out.print("Yes: " + format(d) +" K/s ");
/* We check for pairs not in m. */
ms = System.currentTimeMillis();
for(i = 0; i < n; i++) m.contains(nk[i]);
d = 1.0 * n / (System.currentTimeMillis() - ms);
if (j > 2) totNo += d;
System.out.print("No: " + format(d) +" K/s ");
/* We iterate on m. */
KEY_LIST_ITERATOR it = (KEY_LIST_ITERATOR)m.iterator();
ms = System.currentTimeMillis();
for(; it.hasNext(); it.NEXT_KEY());
d = 1.0 * n / (System.currentTimeMillis() - ms);
if (j > 2) totIterFor += d;
System.out.print("IterFor: " + format(d) +" K/s ");
/* We iterate back on m. */
ms = System.currentTimeMillis();
for(; it.hasPrevious(); it.PREV_KEY());
d = 1.0 * n / (System.currentTimeMillis() - ms);
if (j > 2) totIterBack += d;
System.out.print("IterBack: " + format(d) +" K/s ");
System.out.println();
}
System.out.println();
System.out.println("fastutil Add: " + format(totAdd/(j-3)) + " K/s RemYes: " + format(totRemYes/(j-3)) + " K/s Yes: " + format(totYes/(j-3)) + " K/s No: " + format(totNo/(j-3)) + " K/s IterFor: " + format(totIterFor/(j-3)) + " K/s IterBack: " + format(totIterBack/(j-3)) + "K/s");
System.out.println();
}
private static void fatal(String msg) {
throw new AssertionError(msg);
}
private static void ensure(boolean cond, String msg) {
if (cond) return;
fatal(msg);
}
private static Object[] k, v, nk;
private static KEY_TYPE kt[];
private static KEY_TYPE nkt[];
private static RB_TREE_SET topSet;
protected static void testSets(SORTED_SET m, SortedSet t, int n, int level) throws Exception {
long ms;
boolean mThrowsIllegal, tThrowsIllegal, mThrowsNoElement, tThrowsNoElement;
boolean rt = false, rm = false;
if (level > 4) return;
/* Now we check that both sets agree on first/last keys. */
mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;
try {
m.first();
}
catch (NoSuchElementException e) { mThrowsNoElement = true; }
try {
t.first();
}
catch (NoSuchElementException e) { tThrowsNoElement = true; }
ensure(mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): first() divergence at start in NoSuchElementException (" + mThrowsNoElement + ", " + tThrowsNoElement + ")");
if (! mThrowsNoElement) ensure(t.first().equals(m.first()), "Error (" + level + ", " + seed + "): m and t differ at start on their first key (" + m.first() + ", " + t.first() +")");
mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;
try {
m.last();
}
catch (NoSuchElementException e) { mThrowsNoElement = true; }
try {
t.last();
}
catch (NoSuchElementException e) { tThrowsNoElement = true; }
ensure(mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): last() divergence at start in NoSuchElementException (" + mThrowsNoElement + ", " + tThrowsNoElement + ")");
if (! mThrowsNoElement) ensure(t.last().equals(m.last()), "Error (" + level + ", " + seed + "): m and t differ at start on their last key (" + m.last() + ", " + t.last() +")");
/* Now we check that m and t are equal. */
if (!m.equals(t) || ! t.equals(m)) System.err.println("m: " + m + " t: " + t);
ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) at start");
ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) at start");
/* Now we check that m actually holds that data. */
for(Iterator i=t.iterator(); i.hasNext();) {
ensure(m.contains(i.next()), "Error (" + level + ", " + seed + "): m and t differ on an entry after insertion (iterating on t)");
}
/* Now we check that m actually holds that data, but iterating on m. */
for(Iterator i=m.iterator(); i.hasNext();) {
ensure(t.contains(i.next()), "Error (" + level + ", " + seed + "): m and t differ on an entry after insertion (iterating on m)");
}
/* Now we check that inquiries about random data give the same answer in m and t. For
m we use the polymorphic method. */
for(int i=0; i 0) {
badPrevious = true;
j.previous();
break;
}
previous = k;
}
i = (it.unimi.dsi.fastutil.BidirectionalIterator)m.iterator(from);
for(int k = 0; k < 2*n; k++) {
ensure(i.hasNext() == j.hasNext(), "Error (" + level + ", " + seed + "): divergence in hasNext() (iterator with starting point " + from + ")");
ensure(i.hasPrevious() == j.hasPrevious() || badPrevious && (i.hasPrevious() == (previous != null)), "Error (" + level + ", " + seed + "): divergence in hasPrevious() (iterator with starting point " + from + ")" + badPrevious);
if (r.nextFloat() < .8 && i.hasNext()) {
ensure((I = i.next()).equals(J = j.next()), "Error (" + level + ", " + seed + "): divergence in next() (" + I + ", " + J + ", iterator with starting point " + from + ")");
//System.err.println("Done next " + I + " " + J + " " + badPrevious);
badPrevious = false;
if (r.nextFloat() < 0.5) {
//System.err.println("Removing in next");
i.remove();
j.remove();
t.remove(J);
}
}
else if (!badPrevious && r.nextFloat() < .2 && i.hasPrevious()) {
ensure((I = i.previous()).equals(J = j.previous()), "Error (" + level + ", " + seed + "): divergence in previous() (" + I + ", " + J + ", iterator with starting point " + from + ")");
if (r.nextFloat() < 0.5) {
//System.err.println("Removing in prev");
i.remove();
j.remove();
t.remove(J);
}
}
}
}
/* Now we check that m actually holds that data. */
ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) after iteration");
ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) after iteration");
/* Now we select a pair of keys and create a subset. */
if (! m.isEmpty()) {
java.util.ListIterator i;
Object start = m.first(), end = m.first();
for(i = (java.util.ListIterator)m.iterator(); i.hasNext() && r.nextFloat() < .3; start = end = i.next());
for(; i.hasNext() && r.nextFloat() < .95; end = i.next());
//System.err.println("Checking subSet from " + start + " to " + end + " (level=" + (level+1) + ")...");
testSets((SORTED_SET)m.subSet((KEY_CLASS)start, (KEY_CLASS)end), t.subSet(start, end), n, level + 1);
ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) after subSet");
ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) after subSet");
//System.err.println("Checking headSet to " + end + " (level=" + (level+1) + ")...");
testSets((SORTED_SET)m.headSet((KEY_CLASS)end), t.headSet(end), n, level + 1);
ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) after headSet");
ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) after headSet");
//System.err.println("Checking tailSet from " + start + " (level=" + (level+1) + ")...");
testSets((SORTED_SET)m.tailSet((KEY_CLASS)start), t.tailSet(start), n, level + 1);
ensure(m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals(t) after tailSet");
ensure(t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals(m) after tailSet");
}
}
private static void runTest(int n) throws Exception {
RB_TREE_SET m = new RB_TREE_SET();
SortedSet t = new java.util.TreeSet();
topSet = m;
k = new Object[n];
nk = new Object[n];
kt = new KEY_TYPE[n];
nkt = new KEY_TYPE[n];
for(int i = 0; i < n; i++) {
#if KEY_CLASS_Object
k[i] = kt[i] = genKey();
nk[i] = nkt[i] = genKey();
#else
k[i] = new KEY_CLASS(kt[i] = genKey());
nk[i] = new KEY_CLASS(nkt[i] = genKey());
#endif
}
/* We add pairs to t. */
for(int i = 0; i < n; i++) t.add(k[i]);
/* We add to m the same data */
m.addAll(t);
testSets(m, t, n, 0);
System.out.println("Test OK");
return;
}
public static void main(String args[]) throws Exception {
int n = Integer.parseInt(args[1]);
if (args.length > 2) r = new java.util.Random(seed = Long.parseLong(args[2]));
try {
if ("speedTest".equals(args[0]) || "speedComp".equals(args[0])) speedTest(n, "speedComp".equals(args[0]));
else if ("test".equals(args[0])) runTest(n);
} catch(Throwable e) {
e.printStackTrace(System.err);
System.err.println("seed: " + seed);
throw e;
}
}
#endif
}