All Downloads are FREE. Search and download functionalities are using the official Maven repository.

drv.RBTreeMap.drv Maven / Gradle / Ivy

Go to download

fastutil extends the Java Collections Framework by providing type-specific maps, sets, lists and priority queues with a small memory footprint and fast access and insertion; provides also big (64-bit) arrays, sets and lists, and fast, practical I/O classes for binary and text files.

There is a newer version: 8.5.15
Show newest version
/*		 
 * 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 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 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 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 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 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 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 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 }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy