drv.RBTreeMap.drv Maven / Gradle / Ivy
Show all versions of fastutil Show documentation
/*
* Copyright (C) 2002-2016 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 it.unimi.dsi.fastutil.objects.AbstractObjectSortedSet;
import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import it.unimi.dsi.fastutil.objects.ObjectSortedSet;
import VALUE_PACKAGE.VALUE_COLLECTION;
import VALUE_PACKAGE.VALUE_ABSTRACT_COLLECTION;
import VALUE_PACKAGE.VALUE_ITERATOR;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.NoSuchElementException;
#if VALUES_PRIMITIVE
import VALUE_PACKAGE.VALUE_LIST_ITERATOR;
#endif
/** A type-specific red-black tree map with a fast, small-footprint implementation.
*
* The iterators provided by the views of this class are type-specific {@linkplain
* it.unimi.dsi.fastutil.BidirectionalIterator bidirectional iterators}.
* Moreover, the iterator returned by iterator()
can be safely cast
* to a type-specific {@linkplain java.util.ListIterator list iterator}.
*
*/
public class RB_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC implements java.io.Serializable, Cloneable {
/** A reference to the root entry. */
protected transient Entry KEY_VALUE_GENERIC tree;
/** Number of entries in this map. */
protected int count;
/** The first key in this map. */
protected transient Entry KEY_VALUE_GENERIC firstEntry;
/** The last key in this map. */
protected transient Entry KEY_VALUE_GENERIC lastEntry;
/** Cached set of entries. */
protected transient ObjectSortedSet entries;
/** Cached set of keys. */
protected transient SORTED_SET KEY_GENERIC keys;
/** Cached collection of values. */
protected transient VALUE_COLLECTION VALUE_GENERIC values;
/** The value of this variable remembers, after a put()
* or a remove()
, whether the domain of the map
* has been modified. */
protected transient boolean modified;
/** This map's comparator, as provided in the constructor. */
protected Comparator super KEY_GENERIC_CLASS> storedComparator;
/** This map'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 = -7046029254386353129L;
private static final boolean ASSERTS = ASSERTS_VALUE;
{
allocatePaths();
}
/** Creates a new empty tree map.
*/
public RB_TREE_MAP() {
tree = null;
count = 0;
}
/** Generates the comparator that will be actually used.
*
* When a specific {@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 generate on-the-fly an anonymous class that wraps the non-specific {@link Comparator}
* and makes it into a type-specific one.
*/
private void setActualComparator() {
#if KEY_CLASS_Object
actualComparator = storedComparator;
#else
/* If the provided comparator is already type-specific, we use it. Otherwise,
we use a wrapper anonymous class to fake that it is type-specific. */
if ( storedComparator == null || storedComparator instanceof KEY_COMPARATOR ) actualComparator = (KEY_COMPARATOR)storedComparator;
else actualComparator = new KEY_COMPARATOR KEY_SUPER_GENERIC() {
public int compare( KEY_GENERIC_TYPE k1, KEY_GENERIC_TYPE k2 ) {
return storedComparator.compare( KEY2OBJ( k1 ), KEY2OBJ( k2 ) );
}
public int compare( KEY_GENERIC_CLASS ok1, KEY_GENERIC_CLASS ok2 ) {
return storedComparator.compare( ok1, ok2 );
}
};
#endif
}
/** Creates a new empty tree map with the given comparator.
*
* @param c a (possibly type-specific) comparator.
*/
public RB_TREE_MAP( final Comparator super KEY_GENERIC_CLASS> c ) {
this();
storedComparator = c;
setActualComparator();
}
/** Creates a new tree map copying a given map.
*
* @param m a {@link Map} to be copied into the new tree map.
*/
public RB_TREE_MAP( final Map extends KEY_GENERIC_CLASS, ? extends VALUE_GENERIC_CLASS> m ) {
this();
putAll( m );
}
/** Creates a new tree map copying a given sorted map (and its {@link Comparator}).
*
* @param m a {@link SortedMap} to be copied into the new tree map.
*/
public RB_TREE_MAP( final SortedMap m ) {
this( m.comparator() );
putAll( m );
}
/** Creates a new tree map copying a given map.
*
* @param m a type-specific map to be copied into the new tree map.
*/
public RB_TREE_MAP( final MAP KEY_VALUE_EXTENDS_GENERIC m ) {
this();
putAll( m );
}
/** Creates a new tree map copying a given sorted map (and its {@link Comparator}).
*
* @param m a type-specific sorted map to be copied into the new tree map.
*/
public RB_TREE_MAP( final SORTED_MAP KEY_VALUE_GENERIC m ) {
this( m.comparator() );
putAll( m );
}
/** Creates a new tree map using the elements of two parallel arrays and the given comparator.
*
* @param k the array of keys of the new tree map.
* @param v the array of corresponding values in the new tree map.
* @param c a (possibly type-specific) comparator.
* @throws IllegalArgumentException if k
and v
have different lengths.
*/
public RB_TREE_MAP( final KEY_GENERIC_TYPE[] k, final VALUE_GENERIC_TYPE v[], final Comparator super KEY_GENERIC_CLASS> c ) {
this( c );
if ( k.length != v.length ) throw new IllegalArgumentException( "The key array and the value array have different lengths (" + k.length + " and " + v.length + ")" );
for( int i = 0; i < k.length; i++ ) this.put( k[ i ], v[ i ] );
}
/** Creates a new tree map using the elements of two parallel arrays.
*
* @param k the array of keys of the new tree map.
* @param v the array of corresponding values in the new tree map.
* @throws IllegalArgumentException if k
and v
have different lengths.
*/
public RB_TREE_MAP( final KEY_GENERIC_TYPE[] k, final VALUE_GENERIC_TYPE v[] ) {
this( k, v, null );
}
/*
* The following methods implements some basic building blocks used by
* all accessors. They are (and should be maintained) identical to those used in RBTreeSet.drv.
*
* The put()/remove() code is derived from Ben Pfaff's GNU libavl
* (http://www.msu.edu/~pfaffben/avl/). 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-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; null
, otherwise.
*
* @param k the key to search for.
* @return the corresponding entry, or null
if no entry with the given key exists.
*/
final Entry KEY_VALUE_GENERIC findKey( final KEY_GENERIC_TYPE k ) {
Entry KEY_VALUE_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_VALUE_GENERIC locateKey( final KEY_GENERIC_TYPE k ) {
Entry KEY_VALUE_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_VALUE_GENERIC nodePath[];
SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
private void allocatePaths() {
dirPath = new boolean[ 64 ];
nodePath = new Entry[ 64 ];
}
#if VALUES_PRIMITIVE && !VALUE_CLASS_Boolean
/** Adds an increment to value currently associated with a key.
*
*
Note that this method respects the {@linkplain #defaultReturnValue() default return value} semantics: when
* called with a key that does not currently appears in the map, the key
* will be associated with the default return value plus
* the given increment.
*
* @param k the key.
* @param incr the increment.
* @return the old value, or the {@linkplain #defaultReturnValue() default return value} if no value was present for the given key.
*/
public VALUE_GENERIC_TYPE addTo( final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE incr) {
Entry KEY_VALUE_GENERIC e = add( k );
final VALUE_GENERIC_TYPE oldValue = e.value;
e.value += incr;
return oldValue;
}
#endif
public VALUE_GENERIC_TYPE put( final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE v ) {
Entry KEY_VALUE_GENERIC e = add( k );
final VALUE_GENERIC_TYPE oldValue = e.value;
e.value = v;
return oldValue;
}
/** Returns a node with key k in the balanced tree, creating one with defRetValue if necessary.
*
* @param k the key
* @return a node with key k. If a node with key k already exists, then that node is returned,
* otherwise a new node with defRetValue is created ensuring that the tree is balanced
after creation of the node.
*/
private Entry KEY_VALUE_GENERIC add( final KEY_GENERIC_TYPE k ) {
/* After execution of this method, modified is true iff a new entry has
been inserted. */
modified = false;
int maxDepth = 0;
Entry KEY_VALUE_GENERIC e;
if ( tree == null ) { // The case of the empty tree is treated separately.
count++;
e = tree = lastEntry = firstEntry = new Entry KEY_VALUE_GENERIC( k, defRetValue );
}
else {
Entry KEY_VALUE_GENERIC p = tree;
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 p;
}
nodePath[ i ] = p;
if ( dirPath[ i++ ] = cmp > 0 ) {
if ( p.succ() ) {
count++;
e = new Entry KEY_VALUE_GENERIC( k, defRetValue );
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_VALUE_GENERIC( k, defRetValue );
if ( p.left == null ) firstEntry = e;
e.right = p;
e.left = p.left;
p.left( e );
break;
}
p = p.left;
}
}
modified = true;
maxDepth = i--;
while( i > 0 && ! nodePath[ i ].black() ) {
if ( ! dirPath[ i - 1 ] ) {
Entry KEY_VALUE_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_VALUE_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_VALUE_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_VALUE_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;
if ( ASSERTS ) {
checkNodePath();
checkTree( tree, 0, -1 );
}
return e;
}
/* After execution of this method, {@link #modified} is true iff an entry
has been deleted. */
SUPPRESS_WARNINGS_KEY_UNCHECKED
public VALUE_GENERIC_TYPE REMOVE_VALUE( final KEY_TYPE k ) {
modified = false;
if ( tree == null ) return defRetValue;
Entry KEY_VALUE_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 defRetValue;
}
}
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 defRetValue;
}
}
}
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_VALUE_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_VALUE_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_VALUE_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_VALUE_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_VALUE_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_VALUE_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_VALUE_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 );
}
modified = true;
count--;
// We clean up the node path, or we could have stale references later.
while( maxDepth-- != 0 ) nodePath[ maxDepth ] = null;
if ( ASSERTS ) {
checkNodePath();
checkTree( tree, 0, -1 );
}
return p.value;
}
#if ! KEY_CLASS_Object || VALUES_PRIMITIVE
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
@Override
public VALUE_GENERIC_CLASS put( final KEY_GENERIC_CLASS ok, final VALUE_GENERIC_CLASS ov ) {
final VALUE_GENERIC_TYPE oldValue = put( KEY_CLASS2TYPE(ok), VALUE_CLASS2TYPE(ov) );
return modified ? OBJECT_DEFAULT_RETURN_VALUE : VALUE2OBJ( oldValue );
}
#endif
#if ! KEY_CLASS_Object || VALUES_PRIMITIVE
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
@Override
public VALUE_GENERIC_CLASS remove( final Object ok ) {
final VALUE_GENERIC_TYPE oldValue = REMOVE_VALUE( KEY_OBJ2TYPE( ok ) );
return modified ? VALUE2OBJ( oldValue ) : OBJECT_DEFAULT_RETURN_VALUE;
}
#endif
public boolean containsValue( final VALUE_TYPE v ) {
final ValueIterator i = new ValueIterator();
VALUE_TYPE ev;
int j = count;
while( j-- != 0 ) {
ev = i.NEXT_VALUE();
if ( VALUE_EQUALS( ev, v ) ) return true;
}
return false;
}
public void clear() {
count = 0;
tree = null;
entries = null;
values = null;
keys = null;
firstEntry = lastEntry = null;
}
/** This class represent an entry in a tree map.
*
*
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_VALUE_GENERIC implements Cloneable, MAP.Entry KEY_VALUE_GENERIC {
/** The the bit in this mask is true, the node is black. */
private final static int BLACK_MASK = 1;
/** If the bit in this mask is true, {@link #right} points to a successor. */
private final static int SUCC_MASK = 1 << 31;
/** If the bit in this mask is true, {@link #left} points to a predecessor. */
private final static int PRED_MASK = 1 << 30;
/** The key of this entry. */
KEY_GENERIC_TYPE key;
/** The value of this entry. */
VALUE_GENERIC_TYPE value;
/** The pointers to the left and right subtrees. */
Entry KEY_VALUE_GENERIC left, right;
/** This integers holds different information in different bits (see {@link #SUCC_MASK} and {@link #PRED_MASK}. */
int info;
Entry() {}
/** Creates a new entry with the given key and value.
*
* @param k a key.
* @param v a value.
*/
Entry( final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE v ) {
this.key = k;
this.value = v;
info = SUCC_MASK | PRED_MASK;
}
/** Returns the left subtree.
*
* @return the left subtree (null
if the left
* subtree is empty).
*/
Entry KEY_VALUE_GENERIC left() {
return ( info & PRED_MASK ) != 0 ? null : left;
}
/** Returns the right subtree.
*
* @return the right subtree (null
if the right
* subtree is empty).
*/
Entry KEY_VALUE_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_VALUE_GENERIC pred ) {
info |= PRED_MASK;
left = pred;
}
/** Sets the right pointer to a successor.
* @param succ the successor.
*/
void succ( final Entry KEY_VALUE_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_VALUE_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_VALUE_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 (null
) if this is the last entry).
*/
Entry KEY_VALUE_GENERIC next() {
Entry KEY_VALUE_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 (null
) if this is the first entry).
*/
Entry KEY_VALUE_GENERIC prev() {
Entry KEY_VALUE_GENERIC prev = this.left;
if ( ( info & PRED_MASK ) == 0 ) while ( ( prev.info & SUCC_MASK ) == 0 ) prev = prev.right;
return prev;
}
#if KEYS_PRIMITIVE
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
#endif
public KEY_GENERIC_CLASS getKey() {
return KEY2OBJ(key);
}
#if ! KEY_CLASS_Object
public KEY_GENERIC_TYPE ENTRY_GET_KEY() {
return key;
}
#endif
#if VALUES_PRIMITIVE
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
#endif
public VALUE_GENERIC_CLASS getValue() {
return VALUE2OBJ(value);
}
#if VALUES_PRIMITIVE
public VALUE_TYPE ENTRY_GET_VALUE() {
return value;
}
#endif
public VALUE_GENERIC_TYPE setValue(final VALUE_GENERIC_TYPE value) {
final VALUE_GENERIC_TYPE oldValue = this.value;
this.value = value;
return oldValue;
}
#if VALUES_PRIMITIVE
public VALUE_GENERIC_CLASS setValue(final VALUE_GENERIC_CLASS value) {
return VALUE2OBJ(setValue(VALUE_CLASS2TYPE(value)));
}
#endif
SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
public Entry KEY_VALUE_GENERIC clone() {
Entry KEY_VALUE_GENERIC c;
try {
c = (Entry KEY_VALUE_GENERIC)super.clone();
}
catch(CloneNotSupportedException cantHappen) {
throw new InternalError();
}
c.key = key;
c.value = value;
c.info = info;
return c;
}
@SuppressWarnings("unchecked")
public boolean equals( final Object o ) {
if (!(o instanceof Map.Entry)) return false;
Map.Entry e = (Map.Entry )o;
return KEY_EQUALS( key, KEY_CLASS2TYPE( e.getKey() ) ) && VALUE_EQUALS( value, VALUE_CLASS2TYPE( e.getValue() ) );
}
public int hashCode() {
return KEY2JAVAHASH_NOT_NULL(key) ^ VALUE2JAVAHASH(value);
}
public String toString() {
return key + "=>" + value;
}
/*
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 + "=" + value + " (" + balance() + ")");
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();
}*/
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean containsKey( final KEY_TYPE k ) {
return findKey( KEY_GENERIC_CAST k ) != null;
}
public int size() {
return count;
}
public boolean isEmpty() {
return count == 0;
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
public VALUE_GENERIC_TYPE GET_VALUE( final KEY_TYPE k ) {
final Entry KEY_VALUE_GENERIC e = findKey( KEY_GENERIC_CAST k );
return e == null ? defRetValue : e.value;
}
#if KEY_CLASS_Object && VALUES_PRIMITIVE
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
@Override
SUPPRESS_WARNINGS_KEY_UNCHECKED
public VALUE_GENERIC_CLASS get( final Object ok ) {
final Entry KEY_VALUE_GENERIC e = findKey( KEY_GENERIC_CAST ok );
return e == null ? OBJECT_DEFAULT_RETURN_VALUE : e.getValue();
}
#endif
public KEY_GENERIC_TYPE FIRST_KEY() {
if ( tree == null ) throw new NoSuchElementException();
return firstEntry.key;
}
public KEY_GENERIC_TYPE LAST_KEY() {
if ( tree == null ) throw new NoSuchElementException();
return lastEntry.key;
}
/** An abstract iterator on the whole range.
*
* This class can iterate in both directions on a threaded tree.
*/
private class TreeIterator {
/** The entry that will be returned by the next call to {@link java.util.ListIterator#previous()} (or null
if no previous entry exists). */
Entry KEY_VALUE_GENERIC prev;
/** The entry that will be returned by the next call to {@link java.util.ListIterator#next()} (or null
if no next entry exists). */
Entry KEY_VALUE_GENERIC next;
/** The last entry that was returned (or null
if we did not iterate or used {@link #remove()}). */
Entry KEY_VALUE_GENERIC curr;
/** The current index (in the sense of a {@link java.util.ListIterator}). Note that this value is not meaningful when this {@link TreeIterator} has been created using the nonempty constructor.*/
int index = 0;
TreeIterator() {
next = firstEntry;
}
TreeIterator( 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();
}
}
public boolean hasNext() { return next != null; }
public boolean hasPrevious() { return prev != null; }
void updateNext() {
next = next.next();
}
Entry KEY_VALUE_GENERIC nextEntry() {
if ( ! hasNext() ) throw new NoSuchElementException();
curr = prev = next;
index++;
updateNext();
return curr;
}
void updatePrevious() {
prev = prev.prev();
}
Entry KEY_VALUE_GENERIC previousEntry() {
if ( ! hasPrevious() ) throw new NoSuchElementException();
curr = next = prev;
index--;
updatePrevious();
return curr;
}
public int nextIndex() {
return index;
}
public int previousIndex() {
return index - 1;
}
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_MAP.this.REMOVE_VALUE( curr.key );
curr = null;
}
public int skip( final int n ) {
int i = n;
while( i-- != 0 && hasNext() ) nextEntry();
return n - i - 1;
}
public int back( final int n ) {
int i = n;
while( i-- != 0 && hasPrevious() ) previousEntry();
return n - i - 1;
}
}
/** An iterator on the whole range.
*
*
This class can iterate in both directions on a threaded tree.
*/
private class EntryIterator extends TreeIterator implements ObjectListIterator {
EntryIterator() {}
EntryIterator( final KEY_GENERIC_TYPE k ) {
super( k );
}
public MAP.Entry KEY_VALUE_GENERIC next() { return nextEntry(); }
public MAP.Entry KEY_VALUE_GENERIC previous() { return previousEntry(); }
public void set( MAP.Entry KEY_VALUE_GENERIC ok ) { throw new UnsupportedOperationException(); }
public void add( MAP.Entry KEY_VALUE_GENERIC ok ) { throw new UnsupportedOperationException(); }
}
public ObjectSortedSet ENTRYSET() {
if ( entries == null ) entries = new AbstractObjectSortedSet() {
final Comparator super MAP.Entry KEY_VALUE_GENERIC> comparator = new Comparator () {
public int compare( final MAP.Entry KEY_VALUE_GENERIC x, MAP.Entry KEY_VALUE_GENERIC y ) {
return RB_TREE_MAP.this.actualComparator.compare( x.ENTRY_GET_KEY(), y.ENTRY_GET_KEY() );
}
};
public Comparator super MAP.Entry KEY_VALUE_GENERIC> comparator() {
return comparator;
}
public ObjectBidirectionalIterator iterator() {
return new EntryIterator();
}
public ObjectBidirectionalIterator iterator( final MAP.Entry KEY_VALUE_GENERIC from ) {
return new EntryIterator( from.ENTRY_GET_KEY() );
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean contains( final Object o ) {
if (!(o instanceof Map.Entry)) return false;
final Map.Entry,?> e = (Map.Entry,?>)o;
#if KEYS_PRIMITIVE
if (e.getKey() == null || ! (e.getKey() instanceof KEY_CLASS)) return false;
#endif
#if VALUES_PRIMITIVE
if (e.getValue() == null || ! (e.getValue() instanceof VALUE_CLASS)) return false;
#endif
final Entry KEY_VALUE_GENERIC f = findKey( KEY_OBJ2TYPE( KEY_GENERIC_CAST e.getKey() ) );
return e.equals( f );
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean remove( final Object o ) {
if (!(o instanceof Map.Entry)) return false;
final Map.Entry,?> e = (Map.Entry,?>)o;
#if KEYS_PRIMITIVE
if (e.getKey() == null || ! (e.getKey() instanceof KEY_CLASS)) return false;
#endif
#if VALUES_PRIMITIVE
if (e.getValue() == null || ! (e.getValue() instanceof VALUE_CLASS)) return false;
#endif
final Entry KEY_VALUE_GENERIC f = findKey( KEY_OBJ2TYPE( KEY_GENERIC_CAST e.getKey() ) );
if ( f != null ) RB_TREE_MAP.this.REMOVE_VALUE( f.key );
return f != null;
}
public int size() { return count; }
public void clear() { RB_TREE_MAP.this.clear(); }
public MAP.Entry KEY_VALUE_GENERIC first() { return firstEntry; }
public MAP.Entry KEY_VALUE_GENERIC last() { return lastEntry; }
public ObjectSortedSet subSet( MAP.Entry KEY_VALUE_GENERIC from, MAP.Entry KEY_VALUE_GENERIC to ) { return subMap( from.ENTRY_GET_KEY(), to.ENTRY_GET_KEY() ).ENTRYSET(); }
public ObjectSortedSet headSet( MAP.Entry KEY_VALUE_GENERIC to ) { return headMap( to.ENTRY_GET_KEY() ).ENTRYSET(); }
public ObjectSortedSet tailSet( MAP.Entry KEY_VALUE_GENERIC from ) { return tailMap( from.ENTRY_GET_KEY() ).ENTRYSET(); }
};
return entries;
}
/** An iterator on the whole range of keys.
*
* This class can iterate in both directions on the keys of a threaded tree. We
* simply override the {@link java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly
* their type-specific counterparts) so that they return keys instead of entries.
*/
private final class KeyIterator extends TreeIterator implements KEY_LIST_ITERATOR KEY_GENERIC {
public KeyIterator() {}
public KeyIterator( final KEY_GENERIC_TYPE k ) { super( k ); }
public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; }
public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; }
public void set( KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }
public void add( KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }
#if !KEY_CLASS_Object
public KEY_GENERIC_CLASS next() { return KEY2OBJ( nextEntry().key ); }
public KEY_GENERIC_CLASS previous() { return KEY2OBJ( previousEntry().key ); }
public void set( KEY_CLASS ok ) { throw new UnsupportedOperationException(); }
public void add( KEY_CLASS ok ) { throw new UnsupportedOperationException(); }
#endif
};
/** A keyset implementation using a more direct implementation for iterators. */
private class KeySet extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC.KeySet {
public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new KeyIterator(); }
public KEY_BIDI_ITERATOR KEY_GENERIC iterator( final KEY_GENERIC_TYPE from ) { return new KeyIterator( from ); }
}
/** Returns a type-specific sorted set view of the keys contained in this map.
*
*
In addition to the semantics of {@link java.util.Map#keySet()}, you can
* safely cast the set returned by this call to a type-specific sorted
* set interface.
*
* @return a type-specific sorted set view of the keys contained in this map.
*/
public SORTED_SET KEY_GENERIC keySet() {
if ( keys == null ) keys = new KeySet();
return keys;
}
/** An iterator on the whole range of values.
*
*
This class can iterate in both directions on the values of a threaded tree. We
* simply override the {@link java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly
* their type-specific counterparts) so that they return values instead of entries.
*/
private final class ValueIterator extends TreeIterator implements VALUE_LIST_ITERATOR VALUE_GENERIC {
public VALUE_GENERIC_TYPE NEXT_VALUE() { return nextEntry().value; }
public VALUE_GENERIC_TYPE PREV_VALUE() { return previousEntry().value; }
public void set( VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }
public void add( VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }
#if VALUES_PRIMITIVE
public VALUE_GENERIC_CLASS next() { return VALUE2OBJ( nextEntry().value ); }
public VALUE_GENERIC_CLASS previous() { return VALUE2OBJ( previousEntry().value ); }
public void set( VALUE_CLASS ok ) { throw new UnsupportedOperationException(); }
public void add( VALUE_CLASS ok ) { throw new UnsupportedOperationException(); }
#endif
};
/** Returns a type-specific collection view of the values contained in this map.
*
*
In addition to the semantics of {@link java.util.Map#values()}, you can
* safely cast the collection returned by this call to a type-specific collection
* interface.
*
* @return a type-specific collection view of the values contained in this map.
*/
public VALUE_COLLECTION VALUE_GENERIC values() {
if ( values == null ) values = new VALUE_ABSTRACT_COLLECTION VALUE_GENERIC() {
public VALUE_ITERATOR VALUE_GENERIC iterator() {
return new ValueIterator();
}
public boolean contains( final VALUE_TYPE k ) {
return containsValue( k );
}
public int size() {
return count;
}
public void clear() {
RB_TREE_MAP.this.clear();
}
};
return values;
}
public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() {
return actualComparator;
}
public SORTED_MAP KEY_VALUE_GENERIC headMap( KEY_GENERIC_TYPE to ) {
return new Submap( KEY_NULL, true, to, false );
}
public SORTED_MAP KEY_VALUE_GENERIC tailMap( KEY_GENERIC_TYPE from ) {
return new Submap( from, false, KEY_NULL, true );
}
public SORTED_MAP KEY_VALUE_GENERIC subMap( KEY_GENERIC_TYPE from, KEY_GENERIC_TYPE to ) {
return new Submap( from, false, to, false );
}
/** A submap with given range.
*
*
This class represents a submap. One has to specify the left/right
* limits (which can be set to -∞ or ∞). Since the submap is a
* view on the map, at a given moment it could happen that the limits of
* the range are not any longer in the main map. Thus, things such as
* {@link java.util.SortedMap#firstKey()} or {@link java.util.Collection#size()} must be always computed
* on-the-fly.
*/
private final class Submap extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC implements java.io.Serializable {
private static final long serialVersionUID = -7046029254386353129L;
/** The start of the submap range, unless {@link #bottom} is true. */
KEY_GENERIC_TYPE from;
/** The end of the submap range, unless {@link #top} is true. */
KEY_GENERIC_TYPE to;
/** If true, the submap range starts from -∞. */
boolean bottom;
/** If true, the submap range goes to ∞. */
boolean top;
/** Cached set of entries. */
@SuppressWarnings("hiding")
protected transient ObjectSortedSet entries;
/** Cached set of keys. */
@SuppressWarnings("hiding")
protected transient SORTED_SET KEY_GENERIC keys;
/** Cached collection of values. */
@SuppressWarnings("hiding")
protected transient VALUE_COLLECTION VALUE_GENERIC values;
/** Creates a new submap with given key range.
*
* @param from the start of the submap range.
* @param bottom if true, the first parameter is ignored and the range starts from -∞.
* @param to the end of the submap range.
* @param top if true, the third parameter is ignored and the range goes to ∞.
*/
public Submap( final KEY_GENERIC_TYPE from, final boolean bottom, final KEY_GENERIC_TYPE to, final boolean top ) {
if ( ! bottom && ! top && RB_TREE_MAP.this.compare( from, to ) > 0 ) throw new IllegalArgumentException( "Start key (" + from + ") is larger than end key (" + to + ")" );
this.from = from;
this.bottom = bottom;
this.to = to;
this.top = top;
this.defRetValue = RB_TREE_MAP.this.defRetValue;
}
public void clear() {
final SubmapIterator i = new SubmapIterator();
while( i.hasNext() ) {
i.nextEntry();
i.remove();
}
}
/** Checks whether a key is in the submap range.
* @param k a key.
* @return true if is the key is in the submap range.
*/
final boolean in( final KEY_GENERIC_TYPE k ) {
return ( bottom || RB_TREE_MAP.this.compare( k, from ) >= 0 ) &&
( top || RB_TREE_MAP.this.compare( k, to ) < 0 );
}
public ObjectSortedSet ENTRYSET() {
if ( entries == null ) entries = new AbstractObjectSortedSet() {
public ObjectBidirectionalIterator iterator() {
return new SubmapEntryIterator();
}
public ObjectBidirectionalIterator iterator( final MAP.Entry KEY_VALUE_GENERIC from ) {
return new SubmapEntryIterator( from.ENTRY_GET_KEY() );
}
public Comparator super MAP.Entry KEY_VALUE_GENERIC> comparator() { return RB_TREE_MAP.this.ENTRYSET().comparator(); }
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean contains( final Object o ) {
if (!(o instanceof Map.Entry)) return false;
final Map.Entry,?> e = (Map.Entry,?>)o;
#if KEYS_PRIMITIVE
if (e.getKey() == null || ! (e.getKey() instanceof KEY_CLASS)) return false;
#endif
#if VALUES_PRIMITIVE
if (e.getValue() == null || ! (e.getValue() instanceof VALUE_CLASS)) return false;
#endif
final RB_TREE_MAP.Entry KEY_VALUE_GENERIC f = findKey( KEY_OBJ2TYPE( KEY_GENERIC_CAST e.getKey() ) );
return f != null && in( f.key ) && e.equals( f );
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean remove( final Object o ) {
if (!(o instanceof Map.Entry)) return false;
final Map.Entry,?> e = (Map.Entry,?>)o;
#if KEYS_PRIMITIVE
if (e.getKey() == null || ! (e.getKey() instanceof KEY_CLASS)) return false;
#endif
#if VALUES_PRIMITIVE
if (e.getValue() == null || ! (e.getValue() instanceof VALUE_CLASS)) return false;
#endif
final RB_TREE_MAP.Entry KEY_VALUE_GENERIC f = findKey( KEY_OBJ2TYPE( KEY_GENERIC_CAST e.getKey() ) );
if ( f != null && in( f.key ) ) Submap.this.REMOVE_VALUE( f.key );
return f != null;
}
public int size() {
int c = 0;
for( Iterator> i = iterator(); i.hasNext(); i.next() ) c++;
return c;
}
public boolean isEmpty() { return ! new SubmapIterator().hasNext(); }
public void clear() { Submap.this.clear(); }
public MAP.Entry KEY_VALUE_GENERIC first() { return firstEntry(); }
public MAP.Entry KEY_VALUE_GENERIC last() { return lastEntry(); }
public ObjectSortedSet subSet( MAP.Entry KEY_VALUE_GENERIC from, MAP.Entry KEY_VALUE_GENERIC to ) { return subMap( from.ENTRY_GET_KEY(), to.ENTRY_GET_KEY() ).ENTRYSET(); }
public ObjectSortedSet headSet( MAP.Entry KEY_VALUE_GENERIC to ) { return headMap( to.ENTRY_GET_KEY() ).ENTRYSET(); }
public ObjectSortedSet tailSet( MAP.Entry KEY_VALUE_GENERIC from ) { return tailMap( from.ENTRY_GET_KEY() ).ENTRYSET(); }
};
return entries;
}
private class KeySet extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC.KeySet {
public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubmapKeyIterator(); }
public KEY_BIDI_ITERATOR KEY_GENERIC iterator( final KEY_GENERIC_TYPE from ) { return new SubmapKeyIterator( from ); }
}
public SORTED_SET KEY_GENERIC keySet() {
if ( keys == null ) keys = new KeySet();
return keys;
}
public VALUE_COLLECTION VALUE_GENERIC values() {
if ( values == null ) values = new VALUE_ABSTRACT_COLLECTION VALUE_GENERIC() {
public VALUE_ITERATOR VALUE_GENERIC iterator() {
return new SubmapValueIterator();
}
public boolean contains( final VALUE_TYPE k ) {
return containsValue( k );
}
public int size() {
return Submap.this.size();
}
public void clear() {
Submap.this.clear();
}
};
return values;
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean containsKey( final KEY_TYPE k ) {
return in( KEY_GENERIC_CAST k ) && RB_TREE_MAP.this.containsKey( k );
}
public boolean containsValue( final VALUE_TYPE v ) {
final SubmapIterator i = new SubmapIterator();
VALUE_TYPE ev;
while( i.hasNext() ) {
ev = i.nextEntry().value;
if ( VALUE_EQUALS( ev, v ) ) return true;
}
return false;
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
public VALUE_GENERIC_TYPE GET_VALUE(final KEY_TYPE k) {
final RB_TREE_MAP.Entry KEY_VALUE_GENERIC e;
final KEY_GENERIC_TYPE kk = KEY_GENERIC_CAST k;
return in( kk ) && ( e = findKey( kk ) ) != null ? e.value : this.defRetValue;
}
#if KEY_CLASS_Object && VALUES_PRIMITIVE
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
@Override
SUPPRESS_WARNINGS_KEY_UNCHECKED
public VALUE_GENERIC_CLASS get( final Object ok ) {
final RB_TREE_MAP.Entry KEY_VALUE_GENERIC e;
final KEY_GENERIC_TYPE kk = KEY_GENERIC_CAST KEY_OBJ2TYPE( ok );
return in( kk ) && ( e = findKey( kk ) ) != null ? e.getValue() : OBJECT_DEFAULT_RETURN_VALUE;
}
#endif
public VALUE_GENERIC_TYPE put(final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE v) {
modified = false;
if ( ! in( k ) ) throw new IllegalArgumentException( "Key (" + k + ") out of range [" + ( bottom ? "-" : String.valueOf( from ) ) + ", " + ( top ? "-" : String.valueOf( to ) ) + ")" );
final VALUE_GENERIC_TYPE oldValue = RB_TREE_MAP.this.put( k, v );
return modified ? this.defRetValue : oldValue;
}
#if ! KEY_CLASS_Object || VALUES_PRIMITIVE
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
@Override
public VALUE_GENERIC_CLASS put( final KEY_GENERIC_CLASS ok, final VALUE_GENERIC_CLASS ov ) {
final VALUE_GENERIC_TYPE oldValue = put( KEY_CLASS2TYPE(ok), VALUE_CLASS2TYPE(ov) );
return modified ? OBJECT_DEFAULT_RETURN_VALUE : VALUE2OBJ( oldValue );
}
#endif
SUPPRESS_WARNINGS_KEY_UNCHECKED
public VALUE_GENERIC_TYPE REMOVE_VALUE( final KEY_TYPE k ) {
modified = false;
if ( ! in( KEY_GENERIC_CAST k ) ) return this.defRetValue;
final VALUE_GENERIC_TYPE oldValue = RB_TREE_MAP.this.REMOVE_VALUE( k );
return modified ? oldValue : this.defRetValue;
}
#if ! KEY_CLASS_Object || VALUES_PRIMITIVE
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
@Override
public VALUE_GENERIC_CLASS remove( final Object ok ) {
final VALUE_GENERIC_TYPE oldValue = REMOVE_VALUE( KEY_OBJ2TYPE( ok ) );
return modified ? VALUE2OBJ( oldValue ) : OBJECT_DEFAULT_RETURN_VALUE;
}
#endif
public int size() {
final SubmapIterator i = new SubmapIterator();
int n = 0;
while( i.hasNext() ) {
n++;
i.nextEntry();
}
return n;
}
public boolean isEmpty() {
return ! new SubmapIterator().hasNext();
}
public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() {
return actualComparator;
}
public SORTED_MAP KEY_VALUE_GENERIC headMap( final KEY_GENERIC_TYPE to ) {
if ( top ) return new Submap( from, bottom, to, false );
return compare( to, this.to ) < 0 ? new Submap( from, bottom, to, false ) : this;
}
public SORTED_MAP KEY_VALUE_GENERIC tailMap( final KEY_GENERIC_TYPE from ) {
if ( bottom ) return new Submap( from, false, to, top );
return compare( from, this.from ) > 0 ? new Submap( from, false, to, top ) : this;
}
public SORTED_MAP KEY_VALUE_GENERIC subMap( KEY_GENERIC_TYPE from, KEY_GENERIC_TYPE to ) {
if ( top && bottom ) return new Submap( 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 Submap( from, false, to, false );
}
/** Locates the first entry.
*
* @return the first entry of this submap, or null
if the submap is empty.
*/
public RB_TREE_MAP.Entry KEY_VALUE_GENERIC firstEntry() {
if ( tree == null ) return null;
// If this submap goes to -infinity, we return the main map first entry; otherwise, we locate the start of the map.
RB_TREE_MAP.Entry KEY_VALUE_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 submap 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 submap, or null
if the submap is empty.
*/
public RB_TREE_MAP.Entry KEY_VALUE_GENERIC lastEntry() {
if ( tree == null ) return null;
// If this submap goes to infinity, we return the main map last entry; otherwise, we locate the end of the map.
RB_TREE_MAP.Entry KEY_VALUE_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 submap 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;
}
public KEY_GENERIC_TYPE FIRST_KEY() {
RB_TREE_MAP.Entry KEY_VALUE_GENERIC e = firstEntry();
if ( e == null ) throw new NoSuchElementException();
return e.key;
}
public KEY_GENERIC_TYPE LAST_KEY() {
RB_TREE_MAP.Entry KEY_VALUE_GENERIC e = lastEntry();
if ( e == null ) throw new NoSuchElementException();
return e.key;
}
#if !KEY_CLASS_Object
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
@Override
public KEY_GENERIC_CLASS firstKey() {
RB_TREE_MAP.Entry KEY_VALUE_GENERIC e = firstEntry();
if ( e == null ) throw new NoSuchElementException();
return e.getKey();
}
/** {@inheritDoc}
* @deprecated Please use the corresponding type-specific method instead. */
@Deprecated
@Override
public KEY_GENERIC_CLASS lastKey() {
RB_TREE_MAP.Entry KEY_VALUE_GENERIC e = lastEntry();
if ( e == null ) throw new NoSuchElementException();
return e.getKey();
}
#endif
/** An iterator for subranges.
*
* This class inherits from {@link TreeIterator}, 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 submap we just overwrite the next or previous
* entry with null
.
*/
private class SubmapIterator extends TreeIterator {
SubmapIterator() {
next = firstEntry();
}
SubmapIterator( 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();
}
}
}
void updatePrevious() {
prev = prev.prev();
if ( ! bottom && prev != null && RB_TREE_MAP.this.compare( prev.key, from ) < 0 ) prev = null;
}
void updateNext() {
next = next.next();
if ( ! top && next != null && RB_TREE_MAP.this.compare( next.key, to ) >= 0 ) next = null;
}
}
private class SubmapEntryIterator extends SubmapIterator implements ObjectListIterator {
SubmapEntryIterator() {}
SubmapEntryIterator( final KEY_GENERIC_TYPE k ) {
super( k );
}
public MAP.Entry KEY_VALUE_GENERIC next() { return nextEntry(); }
public MAP.Entry KEY_VALUE_GENERIC previous() { return previousEntry(); }
public void set( MAP.Entry KEY_VALUE_GENERIC ok ) { throw new UnsupportedOperationException(); }
public void add( MAP.Entry KEY_VALUE_GENERIC ok ) { throw new UnsupportedOperationException(); }
}
/** An iterator on a subrange of keys.
*
* This class can iterate in both directions on a subrange of the
* keys of a threaded tree. We simply override the {@link
* java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly their
* type-specific counterparts) so that they return keys instead of
* entries.
*/
private final class SubmapKeyIterator extends SubmapIterator implements KEY_LIST_ITERATOR KEY_GENERIC {
public SubmapKeyIterator() { super(); }
public SubmapKeyIterator( KEY_GENERIC_TYPE from ) { super( from ); }
public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; }
public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; }
public void set( KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }
public void add( KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }
#if !KEY_CLASS_Object
public KEY_GENERIC_CLASS next() { return KEY2OBJ( nextEntry().key ); }
public KEY_GENERIC_CLASS previous() { return KEY2OBJ( previousEntry().key ); }
public void set( KEY_CLASS ok ) { throw new UnsupportedOperationException(); }
public void add( KEY_CLASS ok ) { throw new UnsupportedOperationException(); }
#endif
};
/** An iterator on a subrange of values.
*
*
This class can iterate in both directions on the values of a
* subrange of the keys of a threaded tree. We simply override the
* {@link java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly their
* type-specific counterparts) so that they return values instead of
* entries.
*/
private final class SubmapValueIterator extends SubmapIterator implements VALUE_LIST_ITERATOR VALUE_GENERIC {
public VALUE_GENERIC_TYPE NEXT_VALUE() { return nextEntry().value; }
public VALUE_GENERIC_TYPE PREV_VALUE() { return previousEntry().value; }
public void set( VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }
public void add( VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }
#if VALUES_PRIMITIVE
public VALUE_GENERIC_CLASS next() { return VALUE2OBJ( nextEntry().value ); }
public VALUE_GENERIC_CLASS previous() { return VALUE2OBJ( previousEntry().value ); }
public void set( VALUE_CLASS ok ) { throw new UnsupportedOperationException(); }
public void add( VALUE_CLASS ok ) { throw new UnsupportedOperationException(); }
#endif
};
}
/** Returns a deep copy of this tree map.
*
*
This method performs a deep copy of this tree map; 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 map.
*/
SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
public RB_TREE_MAP KEY_VALUE_GENERIC clone() {
RB_TREE_MAP KEY_VALUE_GENERIC c;
try {
c = (RB_TREE_MAP KEY_VALUE_GENERIC)super.clone();
}
catch(CloneNotSupportedException cantHappen) {
throw new InternalError();
}
c.keys = null;
c.values = null;
c.entries = null;
c.allocatePaths();
if ( count != 0 ) {
// Also this apparently unfathomable code is derived from GNU libavl.
Entry KEY_VALUE_GENERIC e, p, q, rp = new Entry KEY_VALUE_GENERIC(), rq = new Entry KEY_VALUE_GENERIC();
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;
EntryIterator i = new EntryIterator();
Entry KEY_VALUE_GENERIC e;
s.defaultWriteObject();
while(n-- != 0) {
e = i.nextEntry();
s.WRITE_KEY( e.key );
s.WRITE_VALUE( e.value );
}
}
/** 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_VALUE_UNCHECKED
private Entry KEY_VALUE_GENERIC readTree( final java.io.ObjectInputStream s, final int n, final Entry KEY_VALUE_GENERIC pred, final Entry KEY_VALUE_GENERIC succ ) throws java.io.IOException, ClassNotFoundException {
if ( n == 1 ) {
final Entry KEY_VALUE_GENERIC top = new Entry KEY_VALUE_GENERIC( KEY_GENERIC_CAST s.READ_KEY(), VALUE_GENERIC_CAST s.READ_VALUE() );
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_VALUE_GENERIC top = new Entry KEY_VALUE_GENERIC( KEY_GENERIC_CAST s.READ_KEY(), VALUE_GENERIC_CAST s.READ_VALUE() );
top.black( true );
top.right( new Entry KEY_VALUE_GENERIC( KEY_GENERIC_CAST s.READ_KEY(), VALUE_GENERIC_CAST s.READ_VALUE() ) );
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_VALUE_GENERIC top = new Entry KEY_VALUE_GENERIC();
top.left( readTree( s, leftN, pred, top ) );
top.key = KEY_GENERIC_CAST s.READ_KEY();
top.value = VALUE_GENERIC_CAST s.READ_VALUE();
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_VALUE_GENERIC e;
e = tree;
while( e.left() != null ) e = e.left();
firstEntry = e;
e = tree;
while( e.right() != null ) e = e.right();
lastEntry = e;
}
if ( ASSERTS ) checkTree( tree, 0, -1 );
}
#ifdef ASSERTS_CODE
private void checkNodePath() {
for( int i = nodePath.length; i-- != 0; ) assert nodePath[ i ] == null : i;
}
private static KEY_VALUE_GENERIC int checkTree( Entry KEY_VALUE_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;
}
#else
private void checkNodePath() {}
@SuppressWarnings("unused")
private static KEY_VALUE_GENERIC int checkTree( Entry KEY_VALUE_GENERIC e, int d, int D ) { return 0; }
#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 VALUE_TYPE genValue() {
#if VALUE_CLASS_Byte || VALUE_CLASS_Short || VALUE_CLASS_Character
return (VALUE_TYPE)(r.nextInt());
#elif VALUES_PRIMITIVE
return r.NEXT_VALUE();
#elif !VALUE_CLASS_Reference || KEY_CLASS_Reference
return Integer.toBinaryString( r.nextInt() );
#else
return new java.io.Serializable() {};
#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_MAP m;
java.util.TreeMap t;
KEY_TYPE k[] = new KEY_TYPE[n];
KEY_TYPE nk[] = new KEY_TYPE[n];
VALUE_TYPE v[] = new VALUE_TYPE[n];
long ms;
for( i = 0; i < n; i++ ) {
k[i] = genKey();
nk[i] = genKey();
v[i] = genValue();
}
double totPut = 0, totYes = 0, totNo = 0, totAddTo = 0, totIterFor = 0, totIterBack = 0, totRemYes = 0, d, dd, ddd;
if ( comp ) { for( j = 0; j < 20; j++ ) {
t = new java.util.TreeMap();
/* We first add all pairs to t. */
for( i = 0; i < n; i++ ) t.put( KEY2OBJ( k[i] ), VALUE2OBJ( v[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.put( KEY2OBJ( k[i] ), VALUE2OBJ( v[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.put( KEY2OBJ( k[i] ), VALUE2OBJ( v[i] ) );
d += System.currentTimeMillis() - ms;
if ( j > 2 ) totPut += 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.put( KEY2OBJ( k[i] ), VALUE2OBJ( v[i] ) );
#if VALUES_PRIMITIVE && !VALUE_CLASS_Boolean
/* we perform n/2 addTo() operations with get then put */
ms = System.currentTimeMillis();
for( i = 0; i < n/2; i++ ) t.put( KEY2OBJ( k[i] ), (VALUE_TYPE) ((VALUE_CLASS) t.get( KEY2OBJ(k[i])) + i) );
ddd = System.currentTimeMillis() - ms;
if ( j > 2 ) totAddTo += n/ddd;
System.out.print("AddTo: " + format( n/ddd ) +" K/s " );
#endif
/* We check for pairs in t. */
ms = System.currentTimeMillis();
for( i = 0; i < n; i++ ) t.containsKey( 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.containsKey( 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.entrySet().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 Put: " + format( totPut/(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 AddTo: " + format( totAddTo/(j-3) ) + " K/s IterFor: " + format( totIterFor/(j-3) ) + " K/s" );
System.out.println();
t = null;
totPut = totYes = totNo = totIterFor = totIterBack = totRemYes = 0;
}
for( j = 0; j < 20; j++ ) {
m = new RB_TREE_MAP();
/* We first add all pairs to m. */
for( i = 0; i < n; i++ ) m.put( k[i], v[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.put( k[i], v[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.put( k[i], v[i] );
d += System.currentTimeMillis() - ms;
if ( j > 2 ) totPut += 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.put( k[i], v[i] );
#if VALUES_PRIMITIVE && !VALUE_CLASS_Boolean
/* we perform n/2 addTo() operations with get then put */
ms = System.currentTimeMillis();
for( i = 0; i < n/2; i++ ) m.addTo( k[i], (VALUE_TYPE) i );
ddd = System.currentTimeMillis() - ms;
if ( j > 2 ) totAddTo += n/ddd;
System.out.print("AddTo: " + format( n/ddd ) +" K/s " );
#endif
/* We check for pairs in m. */
ms = System.currentTimeMillis();
for( i = 0; i < n; i++ ) m.containsKey( 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.containsKey( 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. */
java.util.ListIterator it = (java.util.ListIterator)m.entrySet().iterator();
ms = System.currentTimeMillis();
for( ; it.hasNext(); it.next() );
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.previous() );
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 Put: " + format( totPut/(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 AddTo: " + format( totAddTo/(j-3) ) + " K/s IterFor: " + format( totIterFor/(j-3) ) + " K/s" );
System.out.println();
}
private static boolean valEquals(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
private static void fatal( String msg ) {
System.out.println( msg );
System.exit( 1 );
}
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 VALUE_TYPE vt[];
private static RB_TREE_MAP topMap;
protected static void testMaps( SORTED_MAP m, SortedMap t, int n, int level ) {
long ms;
boolean mThrowsIllegal, tThrowsIllegal, mThrowsNoElement, tThrowsNoElement;
Object rt = null, rm = null;
if ( level > 4 ) return;
/* Now we check that both maps agree on first/last keys. */
mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;
try {
m.firstKey();
}
catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
try {
t.firstKey();
}
catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): firstKey() divergence at start in NoSuchElementException (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );
if ( ! mThrowsNoElement ) ensure( t.firstKey().equals( m.firstKey() ), "Error (" + level + ", " + seed + "): m and t differ at start on their first key (" + m.firstKey() + ", " + t.firstKey() +")" );
mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;
try {
m.lastKey();
}
catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
try {
t.lastKey();
}
catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): lastKey() divergence at start in NoSuchElementException (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );
if ( ! mThrowsNoElement ) ensure( t.lastKey().equals( m.lastKey() ), "Error (" + level + ", " + seed + "): m and t differ at start on their last key (" + m.lastKey() + ", " + t.lastKey() +")");
/* 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.entrySet().iterator(); i.hasNext(); ) {
java.util.Map.Entry e = (java.util.Map.Entry)i.next();
ensure( valEquals(e.getValue(), m.get(e.getKey())), "Error (" + level + ", " + seed + "): m and t differ on an entry ("+e+") after insertion (iterating on t)" );
}
/* Now we check that m actually holds that data, but iterating on m. */
for(Iterator i=m.entrySet().iterator(); i.hasNext(); ) {
Entry e = (Entry)i.next();
ensure( valEquals(e.getValue(), t.get(e.getKey())), "Error (" + level + ", " + seed + "): m and t differ on an entry ("+e+") after insertion (iterating on m)" );
}
/* Now we check that m actually holds the same keys. */
for(Iterator i=t.keySet().iterator(); i.hasNext(); ) {
Object o = i.next();
ensure( m.containsKey(o), "Error (" + level + ", " + seed + "): m and t differ on a key ("+o+") after insertion (iterating on t)" );
ensure( m.keySet().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a key ("+o+", in keySet()) after insertion (iterating on t)" );
}
/* Now we check that m actually holds the same keys, but iterating on m. */
for(Iterator i=m.keySet().iterator(); i.hasNext(); ) {
Object o = i.next();
ensure( t.containsKey(o), "Error (" + level + ", " + seed + "): m and t differ on a key after insertion (iterating on m)" );
ensure( t.keySet().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a key (in keySet()) after insertion (iterating on m)" );
}
/* Now we check that m actually hold the same values. */
for(Iterator i=t.values().iterator(); i.hasNext(); ) {
Object o = i.next();
ensure( m.containsValue(o), "Error (" + level + ", " + seed + "): m and t differ on a value after insertion (iterating on t)" );
ensure( m.values().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a value (in values()) after insertion (iterating on t)" );
}
/* Now we check that m actually hold the same values, but iterating on m. */
for(Iterator i=m.values().iterator(); i.hasNext(); ) {
Object o = i.next();
ensure( t.containsValue(o), "Error (" + level + ", " + seed + "): m and t differ on a value after insertion (iterating on m)");
ensure( t.values().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a value (in values()) 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)((SORTED_SET)m.keySet()).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 submap. */
if ( ! m.isEmpty() ) {
java.util.ListIterator i;
Object start = m.firstKey(), end = m.firstKey();
for( i = (java.util.ListIterator)m.keySet().iterator(); i.hasNext() && r.nextFloat() < .3; start = end = i.next() );
for( ; i.hasNext() && r.nextFloat() < .95; end = i.next() );
//System.err.println("Checking subMap from " + start + " to " + end + " (level=" + (level+1) + ")..." );
testMaps( (SORTED_MAP)m.subMap( (KEY_CLASS) start, (KEY_CLASS)end ), t.subMap( start, end ), n, level + 1 );
ensure( m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals( t ) after subMap" );
ensure( t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals( m ) after subMap" );
//System.err.println("Checking headMap to " + end + " (level=" + (level+1) + ")..." );
testMaps( (SORTED_MAP)m.headMap( (KEY_CLASS)end ), t.headMap( end ), n, level + 1 );
ensure( m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals( t ) after headMap" );
ensure( t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals( m ) after headMap" );
//System.err.println("Checking tailMap from " + start + " (level=" + (level+1) + ")..." );
testMaps( (SORTED_MAP)m.tailMap( (KEY_CLASS)start ), t.tailMap( start ), n, level + 1 );
ensure( m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals( t ) after tailMap" );
ensure( t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals( m ) after tailMap" );
}
}
private static void test( int n ) {
RB_TREE_MAP m = new RB_TREE_MAP();
SortedMap t = new java.util.TreeMap();
topMap = m;
k = new Object[n];
v = new Object[n];
nk = new Object[n];
kt = new KEY_TYPE[n];
nkt = new KEY_TYPE[n];
vt = new VALUE_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
#if VALUES_REFERENCE
v[i] = vt[i] = genValue();
#else
v[i] = new VALUE_CLASS( vt[i] = genValue() );
#endif
}
/* We add pairs to t. */
for( int i = 0; i < n; i++ ) t.put( k[i], v[i] );
/* We add to m the same data */
m.putAll(t);
testMaps( m, t, n, 0 );
System.out.println("Test OK");
return;
}
public static void main( String args[] ) {
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] ) ) test(n);
} catch( Throwable e ) {
e.printStackTrace( System.err );
System.err.println( "seed: " + seed );
}
}
#endif
}