/*
 * Last update: Tue Jun 30 17:20:18 2009  Doug Lea  (dl at altair)
 * This is a candidate replacement for a class with the
 * following license:
 */

/*
 * Copyright 1997-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */


import java.util.*;
import java.io.*;

/**
 * Hash table based implementation of the <tt>Map</tt> interface.  This
 * implementation provides all of the optional map operations, and permits
 * <tt>null</tt> values and the <tt>null</tt> key.  (The <tt>HashMap</tt>
 * class is roughly equivalent to <tt>Hashtable</tt>, except that it is
 * unsynchronized and permits nulls.)  This class makes no guarantees as to
 * the order of the map; in particular, it does not guarantee that the order
 * will remain constant over time.
 *
 * <p>This implementation provides constant-time performance for the basic
 * operations (<tt>get</tt> and <tt>put</tt>), assuming the hash function
 * disperses the elements properly among the buckets.  Iteration over
 * collection views requires time proportional to the "capacity" of the
 * <tt>HashMap</tt> instance (the number of buckets) plus its size (the number
 * of key-value mappings).  Thus, it's very important not to set the initial
 * capacity too high (or the load factor too low) if iteration performance is
 * important.
 *
 * <p>An instance of <tt>HashMap</tt> has two parameters that affect its
 * performance: <i>initial capacity</i> and <i>load factor</i>.  The
 * <i>capacity</i> is the number of buckets in the hash table, and the initial
 * capacity is simply the capacity at the time the hash table is created.  The
 * <i>load factor</i> is a measure of how full the hash table is allowed to
 * get before its capacity is automatically increased.  When the number of
 * entries in the hash table exceeds the product of the load factor and the
 * current capacity, the hash table is <i>rehashed</i> (that is, internal data
 * structures are rebuilt) so that the hash table has approximately twice the
 * number of buckets.
 *
 * <p>As a general rule, the default load factor (.75) offers a good tradeoff
 * between time and space costs.  Higher values decrease the space overhead
 * but increase the lookup cost (reflected in most of the operations of the
 * <tt>HashMap</tt> class, including <tt>get</tt> and <tt>put</tt>).  The
 * expected number of entries in the map and its load factor should be taken
 * into account when setting its initial capacity, so as to minimize the
 * number of rehash operations.  If the initial capacity is greater
 * than the maximum number of entries divided by the load factor, no
 * rehash operations will ever occur.
 *
 * <p>If many mappings are to be stored in a <tt>HashMap</tt> instance,
 * creating it with a sufficiently large capacity will allow the mappings to
 * be stored more efficiently than letting it perform automatic rehashing as
 * needed to grow the table.
 *
 * <p><strong>Note that this implementation is not synchronized.</strong>
 * If multiple threads access a hash map concurrently, and at least one of
 * the threads modifies the map structurally, it <i>must</i> be
 * synchronized externally.  (A structural modification is any operation
 * that adds or deletes one or more mappings; merely changing the value
 * associated with a key that an instance already contains is not a
 * structural modification.)  This is typically accomplished by
 * synchronizing on some object that naturally encapsulates the map.
 *
 * If no such object exists, the map should be "wrapped" using the
 * {@link Collections#synchronizedMap Collections.synchronizedMap}
 * method.  This is best done at creation time, to prevent accidental
 * unsynchronized access to the map:<pre>
 *   Map m = Collections.synchronizedMap(new HashMap(...));</pre>
 *
 * <p>The iterators returned by all of this class's "collection view methods"
 * are <i>fail-fast</i>: if the map is structurally modified at any time after
 * the iterator is created, in any way except through the iterator's own
 * <tt>remove</tt> method, the iterator will throw a
 * {@link ConcurrentModificationException}.  Thus, in the face of concurrent
 * modification, the iterator fails quickly and cleanly, rather than risking
 * arbitrary, non-deterministic behavior at an undetermined time in the
 * future.
 *
 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
 * as it is, generally speaking, impossible to make any hard guarantees in the
 * presence of unsynchronized concurrent modification.  Fail-fast iterators
 * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
 * Therefore, it would be wrong to write a program that depended on this
 * exception for its correctness: <i>the fail-fast behavior of iterators
 * should be used only to detect bugs.</i>
 *
 * <p>This class is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @param <K> the type of keys maintained by this map
 * @param <V> the type of mapped values
 *
 * @author  Doug Lea
 * @author  Josh Bloch
 * @author  Arthur van Hoff
 * @author  Neal Gafter
 * @see     Object#hashCode()
 * @see     Collection
 * @see     Map
 * @see     TreeMap
 * @see     Hashtable
 * @since   1.2
 */
public class XHashMapV2<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, java.io.Serializable, Cloneable {

    /*
     * Overview: 
     *
     * The main table uses open-addressing, with [key, value] pairs in
     * alternating elements of "kvTable" array, plus a side-array
     * "hashes" holding hashCodes of keys (plus status bit).  It uses
     * hash preconditioning for first probe, and pseudo-random probing
     * from there. This gives a very good approximation of "uniform
     * hashing" (see for example CLR ch12) across various key types.
     *
     * Key removal sometimes requires replacing keys with ABSENT
     * markers.  We minimize need for these by recording (via UNIQUE
     * bit, see below) whether we can instead safely null out entry.
     * Markers are cleared out on put when there are too many of them,
     * as triggered by decrementing "threshold" on adding marker.
     *
     * In steady state, total footprint (ignoring non-dynamic fields)
     * for open tables is less than chained, even with the side array
     * of hashes. At default load factors, the total numbers words is
     * about:
     *                  steady state              initial
     *                  min     ave     max    (default cap)
     * Open:           4.00N   6.00N   8.00N    24
     * Chained:        7.33N   8.00N   8.67N    16 
     *
     * To conserve space for unused maps, arrays are left unallocated
     * upon construction and nulled upon clearing, but with the target
     * allocation size stored in negated form in threshold field,
     * which conveniently requires a (size > threshold) check on
     * insertion anyway.  Additionally, to ensure that tables do not
     * fill up with deletion markers, the threshold is decremented
     * each time an element is replaced with a ABSENT marker.  This
     * forces resize occurring on next insertion to then replace table
     * with one without markers.
     *
     * Maps of size >= CEILING_LENGTH hold overflowing entries in a
     * ternary bitwise trie (see nested class HashTrie). This provides
     * bounded handling without running out of array indices, at the
     * price of about 4X average operation costs and 2.5X per-item
     * space overhead.  The trie is also used for Null keys, which
     * simplifies and speeds up most other table operations. Because
     * operations for Maps allowing nulls don't compose in simple ways
     * (null return values do not reliably mean absent), the HashTrie
     * class is not itself a Map, and so not useful outside this
     * class.
     *
     * Todo:
     *  - improve worst-case iterations for table coverage in nextProbe
     *  - for j.u: use AbstractMap fields, remove unneeded ones
     *  - consider better tiny map support
     *  - Because of lazy init, LinkedHashMap can ignore this
     *    implementation and just use old one without much loss.
     *    This looks like best option.
     *  
     */

    /**
     * The minimum length of the hashes array.  The kvTable array
     * length is twice this value. MUST be a power of two <= 1<<29.
     * Using size less than 8 interacts badly with hash probe
     * randomization.
     */ 
    static final int MINIMUM_LENGTH = 8;

    /**
     * The default length of the hashes array.  The kvTable array
     * length is twice this value. MUST be a power of two <= 1<<29.
     */ 
    static final int DEFAULT_LENGTH = 8;
    
    /**
     * The maximum length of the hashes array.
     * MUST be a power of two <= 1<<29.
     */
    static final int CEILING_LENGTH = 1 << 29;

    /**
     * Sign bit. OR'ed with hashes at index i to indicate that more
     * than one key hashes to this location. This is used in two ways:
     * (1) to avoid need for ABSENT sentinel when removing a key that
     * is UNIQUE'ly mapped, and (2) speeding up searches for
     * non-existent keys -- upon seeing a non-matching unique
     * location, the search can exit.
     */
    static final int UNIQUE = 1 << 31;

    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * Translation scale for load factors that assume chained table
     * implementation. By considering the number of indirect
     * references (not "probes"), factors for chained vs uniform open
     * tables can be roughly equated. The corresponding formulas are:
     *
     * Present keys (common case for get, remove etc):
     *   Chained: (1 + f/2) + 1         ; + 1" for table->node indirection
     *   Open:    ln(1 / (1 - f)) / f   ; f must be < 1 for open 
     * Absent keys (common case for put, plus missed gets etc)
     *   Chained: (1 + f) + 1  
     *   Open:    1 / (1 - f)  
     *
     * Because of UNIQUE bits, the typical performance in Absent case
     * is better than this indicates, but it is still a reasonable
     * basis for translation: placing a ceiling on expected
     * performance for Present keys and then averaging with the ratio
     * for Absent keys (which works out to (f+1) / (f+2)).  In most
     * realistic regions of f, performance in the Present case for
     * open varies only slowly with f, so fixing at ceiling is close
     * enough.  The following conveniently equates these for f = 0.75,
     * which is also about as high you'd ever want to load an open
     * address table. Note that by averaging, the actual load factor
     * ceiling is less than scale.
     */
    static final float LOAD_FACTOR_SCALE = 0.863636f;

    /** Hash code for absent keys, unlikely to clash with real ones  */
    static final int ABSENT_HASH_CODE = 1353688330 & ~UNIQUE;
    
    /** Class for ABSENT sentinel keys */
    static final class Sentinel {
        public final int hashCode() { return ABSENT_HASH_CODE; }
    }

    /** Sentinel for deleted keys, also serving as not-found return val  */
    static final Sentinel ABSENT = new Sentinel();

    /**
     * The table of key-value pairs, lazily constructed and resized as
     * necessary. Length MUST always be a power of two.
     * 
     */
    transient Object[] kvTable;

    /**
     * The bottom 31 bits of (spread)hash codes for keys in the table,
     * OR'ed with UNIQUE when only one key maps to an index. Length
     * MUST always be a power of two.  Constructed on first use.
     */
    transient int[] hashes;

    /**
     * The number of key-value mappings contained in this map.
     */
    transient int size;

    /**
     * The next size value at which to resize
     * @serial
     */
    int threshold;

    /**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).
     */
    transient int modCount;

    /**
     * The load factor for the hash table.
     *
     * @serial
     */
    final float loadFactor; 

    /**
     * HashTrie used for overflow, as well as for null keys
     */
    transient HashTrie trie;

    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and load factor.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public XHashMapV2(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        this.threshold = -initialCapacity; // hold in threshold until resize

        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        // average unsuccessful ratios with ceiling -- see above
        double ratio = (loadFactor + 1.0) / (loadFactor + 2.0);
        this.loadFactor = (float)(0.5 * (ratio + LOAD_FACTOR_SCALE));

        init();
    }

    /**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and the default load factor (0.75).
     *
     * @param  initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    public XHashMapV2(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * and the default load factor (0.75).
     */
    public XHashMapV2() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        this.threshold = -DEFAULT_LENGTH;
        init();
    }

    /**
     * Constructs a new <tt>HashMap</tt> with the same mappings as the
     * specified <tt>Map</tt>.  The <tt>HashMap</tt> is created with
     * default load factor (0.75) and an initial capacity sufficient to
     * hold the mappings in the specified <tt>Map</tt>.
     *
     * @param   m the map whose mappings are to be placed in this map
     * @throws  NullPointerException if the specified map is null
     */
    public XHashMapV2(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        this.threshold = -MINIMUM_LENGTH;
        putAll(m); // will resize
    }

    // internal utilities

    /**
     * Initialization hook for subclasses. This method is called
     * in all constructors and pseudo-constructors (clone, readObject)
     * after HashMap has been initialized but before any entries have
     * been inserted.  (In the absence of this method, readObject would
     * require explicit knowledge of subclasses.)
     */
    void init() {
    }


    /**
     * Applies a supplemental hash function to a given object's
     * hashCode, which defends against poor quality hash functions.
     * Note: The results of this function for search keys must be ORed
     * with UNIQUE.
     */
    static final int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor)
        // while still permitting some locality for small dense hash codes
        h ^= (h >>> 20) ^ (h >>> 12);
        return ~(h ^ (h >>> 7) ^ (h >>> 4));
    }

    /**
     * Returns next index value (to be masked with table size) for
     * psuedo-random probes. Uses Marsaglia Xorshift, that cycles
     * through all non-zero ints, with parameters that work well with
     * hash preconditioning function as initial argument.  The maximum
     * number of sequential calls that yield the same masked table
     * index is at worst 10 for minimum table size (8).
     *
     * @param h previous index MUST be nonzero, which is normally
     * ensured by ORing keys with UNIQUE
     */
    static final int nextProbe(int h) {
        h ^= h << 1; 
        h ^= h >>> 3; 
        return h ^ (h << 10);
    }

    /**
     * Returns the number of key-value mappings in this map.
     *
     * @return the number of key-value mappings in this map
     */
    public int size() {
        return size;
    }

    /**
     * Returns <tt>true</tt> if this map contains no key-value mappings.
     *
     * @return <tt>true</tt> if this map contains no key-value mappings
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     *
     * @see #put(Object, Object)
     */
    public V get(Object key) {
        int hash = UNIQUE;
        if (key != null) {
            hash |= hash(key.hashCode());
            Object[] kvs = kvTable;
            if (kvs != null) {
                int[] hs = hashes;
                int mask = hs.length - 1;
                int h = hash; // unmasked hs index
                Object k;     // key at kvs[j]
                int j;        // index of key (Value in j+1, Hash in hs[j>>>1])
                int s;        // the hash for k, maybe OR'ed with UNIQUE
                while ((k = kvs[j = (h & mask) << 1]) != null) {
                    Object v = kvs[j + 1]; // prefetch value
                    if (k == key || (((s = hs[j >>> 1]) | UNIQUE) == hash && 
                                     k.equals(key)))
                        return (V) v;
                    if (s < 0) 
                        break; // if unique mapping, can stop probing
                    h = nextProbe(h);
                }
            }
        }
        return trie != null? (V) trie.get(hash, key) : null;
    }

    /**
     * Returns <tt>true</tt> if this map contains a mapping for the
     * specified key.
     *
     * @param   key   The key whose presence in this map is to be tested
     * @return <tt>true</tt> if this map contains a mapping for the specified
     * key.
     */
    public boolean containsKey(Object key) {
        // Annoyingly nearly cloned from get
        int hash = UNIQUE;
        if (key != null) {
            hash |= hash(key.hashCode());
            Object[] kvs = kvTable;
            if (kvs != null) {
                int[] hs = hashes;
                int mask = hs.length - 1;
                int h = hash;
                Object k;
                int j;
                int s; 
                while ((k = kvs[j = (h & mask) << 1]) != null) {
                    if (k == key || (((s = hs[j >>> 1]) | UNIQUE) == hash && 
                                     k.equals(key)))
                        return true;
                    if (s < 0) 
                        break; 
                    h = nextProbe(h);
                }
            }
        }
        return trie != null && trie.containsKey(hash, key);
    }

    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        if (key == null)
            return triePut(UNIQUE, null, value); // offload
        int hash = hash(key.hashCode()) | UNIQUE;
        if (size >= threshold)
            resize();
        Object[] kvs = kvTable;
        int[] hs = hashes;
        int mask = hs.length - 1;
        int h = hash;
        int firstDeleted = -1; // to reuse deleted slot
        Object k;
        int j;
        while ((k = kvs[j = (h & mask) << 1]) != null) {
            int s = hs[j >>> 1];
            if (k == key || ((s | UNIQUE) == hash && k.equals(key))) {
                Object v = kvs[j + 1];
                kvs[j + 1] = value;
                return (V)v;
            }
            if (s < 0) // mark index as shared
                hs[j >>> 1] = s & ~UNIQUE;
            else if (k == ABSENT && firstDeleted < 0)
                firstDeleted = j;
            h = nextProbe(h);
        }

        if (trie != null) 
            return triePut(hash, key, value);

        if (firstDeleted >= 0) {
            j = firstDeleted;
            hash &= ~UNIQUE;
            ++threshold; // adjust for reusing deleted slot
        }
        kvs[j] = key;
        kvs[j + 1] = value;
        hs[j >>> 1] = hash;
        ++modCount;
        ++size;
        return null;
    }

    /**
     * Version of put for overflow or null key
     */
    private V triePut(int hash, K key, V value) {
        HashTrie t = trie;
        if (t == null)
            t = trie = new HashTrie();
        Object v = t.put(hash, key, value);
        if (v != ABSENT)
            return (V) v;
        ++modCount;
        ++size;
        return null;
    }

    /**
     * Copies all of the mappings from the specified map to this map.
     * These mappings will replace any mappings that this map had for
     * any of the keys currently in the specified map.
     *
     * @param m mappings to be stored in this map
     * @throws NullPointerException if the specified map is null
     */
    public void putAll(Map<? extends K, ? extends V> m) {
        int n = m.size();
        if (n == 0)
            return;
        int t = threshold;
        if (n > t) {
            if (t < 0) // already holds stored threshold
                t = -t;
            int nt = (int)(n / loadFactor + 1);
            if (nt > t)
                threshold = -nt; // force resize on put
        }
        for (Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    /**
     * Resize the table
     */
    private void resize() {
        Object[] oldKvs = kvTable;
        int[] oldHs = hashes;
        int oldCap = oldHs == null? 0 : oldHs.length;
        int oldThreshold = threshold;

        int cap; 
        if (oldThreshold < 0) // try to use stashed capacity
            cap = -oldThreshold;
        else if (size < oldCap >>> 1) // possibly shrink after deletions 
            cap = (int)((size + (size >>> 1)) / loadFactor + 1);
        else
            cap = oldCap << 1;

        if (cap > CEILING_LENGTH >>> 1) { // overflow
            threshold = Integer.MAX_VALUE; // avoid future resizes
            if (trie == null)
                trie = new HashTrie();
            if (oldHs != null)  
                return;
            else // overflow on init -- use minimal arrays
                cap = MINIMUM_LENGTH;
        }
        else {
            if (cap < MINIMUM_LENGTH || (cap & (cap - 1)) != 0) { 
                int c = cap; // force power of two
                cap = MINIMUM_LENGTH;
                while (cap < c) cap <<= 1;
            }
            threshold = (int)(cap * loadFactor);
        }

        int[] newHs = hashes = new int[cap];
        Object[] newKvs = kvTable =  new Object[cap << 1];

        if (oldHs != null && size != 0) {
            int mask = cap - 1;
            for (int m = 0; m < oldKvs.length; m += 2) {
                Object key = oldKvs[m];
                if (key != null && key != ABSENT) {
                    Object value = oldKvs[m + 1];
                    int hash = oldHs[m >>> 1] | UNIQUE;
                    int h = hash;
                    int j;
                    while (newKvs[j = (h & mask) << 1] != null) {
                        newHs[j >>> 1] &= ~UNIQUE;
                        h = nextProbe(h);
                    }
                    newKvs[j] = key;
                    newKvs[j + 1] = value;
                    newHs[j >>> 1] = hash;
                }
            }
        }
    }

    /**
     * Removes the mapping for the specified key from this map if present.
     *
     * @param  key key whose mapping is to be removed from the map
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V remove(Object key) {
        int hash = UNIQUE;
        if (key != null) {
            hash |= hash(key.hashCode());
            Object v = tableRemove(hash, key, ABSENT);
            if (v != ABSENT) 
                return (V) v;
        }
        if (trie != null) {
            Object v = trie.remove(hash, key, ABSENT);
            if (v != ABSENT) {
                ++modCount;
                --size;
                return (V) v;
            }
        }
        return null;
    }

    /**
     * Remove key only if mapped to target value.
     * (Needed by EntrySets).
     */
    boolean removeMapping(Object key, Object target) {
        int hash = UNIQUE;
        if (key != null) {
            hash |= hash(key.hashCode());
            if (tableRemove(hash, key, target) != ABSENT)
                return true;
        }
        if (trie != null && trie.remove(hash, key, target) != ABSENT) {
            ++modCount;
            --size;
            return true;
        }
        return false;
    }

    /**
     * Remove from table if present
     * @param hash hash for key
     * @param key key
     * @param target if not ABSENT, only remove if key maps to it
     * @return old value, or ABSENT if not found
     */
    final Object tableRemove(int hash, Object key, Object target) {
        Object[] kvs = kvTable;
        if (kvs != null) {
            int[] hs = hashes;
            int mask = hs.length - 1;
            int j;
            Object k;
            int h = hash;
            while ((k = kvs[j = (h & mask) << 1]) != null) {
                int s = hs[j >>> 1];
                if (k == key || ((s | UNIQUE) == hash && k.equals(key))) {
                    Object v = kvs[j + 1];
                    if (target != ABSENT &&
                        (target != v && (target != null || !target.equals(v))))
                        break;
                    ++modCount;
                    if (--size > 0 || !maybeClearOnRemove()) {
                        kvs[j + 1] = null;
                        if (s < 0) { // unique
                            kvs[j] = null;
                            hs[j >>> 1] = 0;
                        }
                        else {
                            kvs[j] = ABSENT;
                            hs[j >>> 1] = ABSENT_HASH_CODE;
                            --threshold;  // to trigger cleaning
                        }
                    }
                    return v;
                }
                if (s < 0)
                    break;
                h = nextProbe(h);
            }
        }
        return ABSENT;
    }

    /**
     * Possibly clear table upon removing last element
     */
    private boolean maybeClearOnRemove() {
        int[] hs = hashes;
        if (hs == null) 
            return true; // already clear
        int cap = hs.length;
        if (threshold >= (int)(cap * loadFactor))
            return false; // no ABSENT items
        threshold = -cap; // record to recreate on put
        hashes = null;
        kvTable = null;
        return true;
    }
        
    /**
     * Removes all of the mappings from this map.
     * The map will be empty after this call returns.
     */
    public void clear() {
        int[] hs = hashes;
        if (hs != null) { // drop tables
            threshold = -hs.length; // record to recreate on put
            hashes = null;
            kvTable = null;
        }
        trie = null;
        modCount++;
        size = 0;
    }

    /**
     * Tests whether the specified object reference is a value in this map.
     *
     * @param value value whose presence in this map is to be tested
     * @return <tt>true</tt> if this map maps one or more keys to the
     *         specified object reference
     * @see     #containsKey(Object)
     */
    public boolean containsValue(Object value) {
        Object[] kvs = kvTable;
        if (kvs != null) {
            for (int j = 0; j < kvs.length; j += 2) {
                Object k = kvs[j];
                Object v = kvs[j + 1];
                if (k != null && k != ABSENT &&
                    (value == v || (value != null && value.equals(v))))
                    return true;
            }
        }
        return trie != null && trie.containsValue(value);
    }

    /**
     * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
     * values themselves are not cloned.
     *
     * @return a shallow copy of this map
     */
    public Object clone() {
        try {
            XHashMapV2<K,V> m = (XHashMapV2<K,V>) super.clone();
            m.trie = null;
            m.hashes = null;
            m.kvTable = null;
            m.threshold = -MINIMUM_LENGTH;
            m.size = 0;
            m.putAll(this);
            return m;
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    }

    abstract class XHashMapV2Iterator<T> implements Iterator<T> {
        Object nextKey;
        int nextIndex;
        int lastReturnedIndex;
        int expectedModCount;
        TrieNode nextNode;
        TrieNode lastReturnedNode;

        XHashMapV2Iterator() {
            expectedModCount = modCount;
            advanceIndex(-2);
        }

        public final boolean hasNext() {
            return nextIndex >= 0 || nextNode != null;
        }

        final void advanceIndex(int j) {
            lastReturnedIndex = j;
            Object[] kvs = kvTable;
            if (kvs != null) {
                Object k;
                while ((j += 2) < kvs.length) {
                    if ((k = kvs[j]) != null && k != ABSENT) {
                        nextKey = k;
                        nextIndex = j;
                        return;
                    }
                }
            }
            nextNode = trie == null? null : trie.first;
            nextIndex = -2;
        }
        
        final K nextKey() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k;
            int j = nextIndex;
            if (j >= 0) {
                k = nextKey;
                advanceIndex(j);
            }
            else if (nextNode != null) {
                k = nextNode.key;
                lastReturnedNode = nextNode;
                nextNode = nextNode.next;
            }
            else
                throw new NoSuchElementException();
            return (K) k;
        }

        final V nextValue() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object v;
            int j = nextIndex;
            if (j >= 0) {
                v = kvTable[j + 1];
                advanceIndex(j);
            }
            else if (nextNode != null) {
                v = nextNode.value;
                lastReturnedNode = nextNode;
                nextNode = nextNode.next;
            }
            else 
                throw new NoSuchElementException();
            return (V)v;
        }

        final IteratorEntry nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k;
            Object v;
            int j = nextIndex;
            if (j >= 0) {
                k = nextKey;
                v = kvTable[j + 1];
                advanceIndex(j);
            }
            else if (nextNode != null) {
                k = nextNode.key;
                v = nextNode.value;
                lastReturnedNode = nextNode;
                nextNode = nextNode.next;
            }
            else
                throw new NoSuchElementException();
            return new IteratorEntry((K)k, (V)v);
        }

        public void remove() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k;
            if (lastReturnedIndex >= 0) {
                k = kvTable[lastReturnedIndex];
                lastReturnedIndex = -1;
            }
            else if (lastReturnedNode != null) {
                k = lastReturnedNode.key;
                lastReturnedNode = null;
            }
            else
                throw new IllegalStateException();
            XHashMapV2.this.remove(k);
            expectedModCount = modCount;
        }
    }

    final class KeyIterator extends XHashMapV2Iterator<K> {
        public final K next() { return super.nextKey(); }
    }

    final class ValueIterator extends XHashMapV2Iterator<V> {
        public final V next() { return super.nextValue(); }
    }

    final class EntryIterator extends XHashMapV2Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return super.nextEntry(); }
    }

    final class IteratorEntry implements Map.Entry<K,V> {
        final K key;
        V value;
        IteratorEntry(K key, V value) {
            this.key = key;
            this.value = value;
        }
        public final K getKey() {
            return key;
        }
        public final V getValue() {
            return value;
        }
        public final V setValue(V newValue) {
            this.value = newValue;
            return put(key, newValue);
        }
        public boolean equals(Object o) {
            if (o instanceof Map.Entry) {
                Map.Entry<K,V> e = (Map.Entry<K,V>)o;
                K ek = e.getKey();
                if (key == ek || (key != null && key.equals(ek))) {
                    V ev = e.getValue();
                    return value == ev || (value != null && value.equals(ev));
                }
            }
            return false;
        }
        public int hashCode() {
            return pairHash(key, value);
        }
        public String toString() {
            return key + "=" + value;
        }
    }

    // Overrides for entry-oriented methods, to speed up traversal a bit

    /**
     * Utility: Return hash code for pair
     */
    private static int pairHash(Object k, Object v) {
        return ((k == null? 0 : k.hashCode()) ^
                (v == null? 0 : v.hashCode()));
    }

    /**
     * Returns the hash code value for this map.  The hash code of a map is
     * defined to be the sum of the hash codes of each entry in the map's
     * <tt>entrySet()</tt> view.  This ensures that <tt>m1.equals(m2)</tt>
     * implies that <tt>m1.hashCode()==m2.hashCode()</tt> for any two maps
     * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of
     * {@link Object#hashCode}.
     *
     * <p>This implementation iterates over <tt>entrySet()</tt>, calling
     * {@link Map.Entry#hashCode hashCode()} on each element (entry) in the
     * set, and adding up the results.
     *
     * @return the hash code value for this map
     * @see Map.Entry#hashCode()
     * @see Object#equals(Object)
     * @see Set#equals(Object)
     */
    public int hashCode() {
        int sum = 0;
        Object[] kvs = kvTable;
        if (kvs != null) {
            Object k;
            for (int j = 0; j < kvs.length; j += 2) {
                if ((k = kvs[j]) != null && k != ABSENT)
                    sum += pairHash(k, kvs[j + 1]);
            }
        }
        if (trie != null) {
            for (TrieNode t = trie.first; t != null; t = t.next)
                sum += pairHash(t.key, t.value);
        }
        return sum;
    }


    /**
     * Utility for entrySets and Map.equals
     * Static to allow calls from other map in equals
     */
    static boolean containsMapping(Map<?,?> m, Object key, Object value) {
        Object v = m.get(key);
        if (value == null)
            return v == null && m.containsKey(key);
        else 
            return value == v || (value != null && value.equals(v));
    }

    /**
     * Compares the specified object with this map for equality.  Returns
     * <tt>true</tt> if the given object is also a map and the two maps
     * represent the same mappings.  More formally, two maps <tt>m1</tt> and
     * <tt>m2</tt> represent the same mappings if
     * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the
     * <tt>equals</tt> method works properly across different implementations
     * of the <tt>Map</tt> interface.
     *
     * <p>This implementation first checks if the specified object is this map;
     * if so it returns <tt>true</tt>.  Then, it checks if the specified
     * object is a map whose size is identical to the size of this map; if
     * not, it returns <tt>false</tt>.  If so, it iterates over this map's
     * <tt>entrySet</tt> collection, and checks that the specified map
     * contains each mapping that this map contains.  If the specified map
     * fails to contain such a mapping, <tt>false</tt> is returned.  If the
     * iteration completes, <tt>true</tt> is returned.
     *
     * @param o object to be compared for equality with this map
     * @return <tt>true</tt> if the specified object is equal to this map
     */
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Map))
            return false;
        Map<K,V> m = (Map<K,V>) o;
        if (m.size() != size)
            return false;
        try {
            Object[] kvs = kvTable;
            if (kvs != null) {
                for (int j = 0; j < kvs.length; j += 2) {
                    Object k = kvs[j];
                    if (k != null && k != ABSENT &&
                        !containsMapping(m, k, kvs[j + 1]))
                        return false;
                }
            }
            if (trie != null) {
                for (TrieNode t = trie.first; t != null; t = t.next) {
                    if (!containsMapping(m, t.key, t.value))
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
        return true;
    }
    

    // Views

    // Delete these in j.u version
    transient Set<K> keySet;       
    transient Collection<V> values;

    /**
     * This field is initialized to contain an instance of the entry set
     * view the first time this view is requested.  The view is stateless,
     * so there's no reason to create more than one.
     */
    transient Set<Map.Entry<K,V>> entrySet;

    /**
     * Returns a {@link Set} view of the keys contained in this map.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  If the map is modified
     * while an iteration over the set is in progress (except through
     * the iterator's own <tt>remove</tt> operation), the results of
     * the iteration are undefined.  The set supports element removal,
     * which removes the corresponding mapping from the map, via the
     * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
     * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
     * operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>
     * operations.
     */
    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }

    final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return new KeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            if (containsKey(o)) {
                XHashMapV2.this.remove(o);
                return true;
            }
            return false;
        }
        public void clear() {
            XHashMapV2.this.clear();
        }
    }

    /**
     * Returns a {@link Collection} view of the values contained in this map.
     * The collection is backed by the map, so changes to the map are
     * reflected in the collection, and vice-versa.  If the map is
     * modified while an iteration over the collection is in progress
     * (except through the iterator's own <tt>remove</tt> operation),
     * the results of the iteration are undefined.  The collection
     * supports element removal, which removes the corresponding
     * mapping from the map, via the <tt>Iterator.remove</tt>,
     * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
     * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not
     * support the <tt>add</tt> or <tt>addAll</tt> operations.
     */
    public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null ? vs : (values = new Values()));
    }

    final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return new ValueIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            XHashMapV2.this.clear();
        }
    }

    /**
     * Returns a {@link Set} view of the mappings contained in this map.
     * The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa.  If the map is modified
     * while an iteration over the set is in progress (except through
     * the iterator's own <tt>remove</tt> operation, or through the
     * <tt>setValue</tt> operation on a map entry returned by the
     * iterator) the results of the iteration are undefined.  The set
     * supports element removal, which removes the corresponding
     * mapping from the map, via the <tt>Iterator.remove</tt>,
     * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
     * <tt>clear</tt> operations.  It does not support the
     * <tt>add</tt> or <tt>addAll</tt> operations.
     *
     * @return a set view of the mappings contained in this map
     */
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es = entrySet;
        return (es != null ? es : (entrySet = new EntrySet()));
    }

    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            return containsMapping(XHashMapV2.this, e.getKey(), e.getValue());
        }
        public boolean remove(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>) o;
            return removeMapping(e.getKey(), e.getValue());
        }
        public int size() {
            return size;
        }
        public void clear() {
            XHashMapV2.this.clear();
        }
    }

    private static final long serialVersionUID = 8188218128353913216L;

    /**
     * Save the state of the <tt>XHashMapV2</tt> instance to a stream
     * (i.e., serialize it).
     *
     * @serialData The <i>size</i> of the HashMap (the number of key-value
     *          mappings) (<tt>int</tt>), followed by the key (Object) and
     *          value (Object) for each key-value mapping represented by the
     *          XHashMapV2.  The key-value mappings are emitted in no
     *          particular order.
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException  {

        if (hashes == null)
            resize(); // force allocation

        Iterator<Map.Entry<K,V>> i =
            (size > 0) ? entrySet().iterator() : null;

        // Write out the threshold, loadfactor, and any hidden stuff
        s.defaultWriteObject();

        // Write out number of buckets
        s.writeInt(hashes.length);

        // Write out size (number of Mappings)
        s.writeInt(size);

        // Write out keys and values (alternating)
        if (i != null) {
            while (i.hasNext()) {
                Map.Entry<K,V> e = i.next();
                s.writeObject(e.getKey());
                s.writeObject(e.getValue());
            }
        }
    }

    /**
     * Reconstitute the instance from a stream (i.e.,
     * deserialize it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException  {
        // Read in the threshold, loadfactor, and any hidden stuff
        s.defaultReadObject();

        // Read in number of buckets
        int numBuckets = s.readInt();

        // Fixme: Mesh with revised linked hash map
        init();  // Give subclass a chance to do its thing.

        // Read in size (number of Mappings)
        int size = s.readInt();
        threshold = -(int)(size / loadFactor + 1);

        // Read the keys and values, and put the mappings in the HashMap
        for (int i=0; i<size; i++) {
            K key = (K) s.readObject();
            V value = (V) s.readObject();
            put(key, value);
        }
    }

    /**
     * Nodes for ternary bitwise trie holding overflow.  See
     * explanation below.
     */
    static final class TrieNode {
        final int hash;
        Object key;
        Object value;
        TrieNode next;
        TrieNode prev;
        TrieNode left;
        TrieNode right;
        TrieNode dups;
        TrieNode(int hash, Object key, Object value) {
            this.hash = hash;
            this.key = key;
            this.value = value;
        }
        Object setValue(Object newValue) {
            Object v = value;
            value = newValue;
            return v;
        }
    }

    /**
     * Bitwise ternary trie for handling overflow, keyed on hash
     * codes.  Each node contains an entry, as well as a link to list
     * of other nodes with same hash code.  Each level of the trie
     * implicitly corresponds to a bit (bit 0 at root). On search, if
     * the key doesn't match, then traversal goes left if the bit at
     * the current level is zero, else right. Note that the path
     * length for any search is bounded by the number of bits (32).
     *
     * To support traversal, nodes also have next/prev pointers
     * that are linked from the list headed at "first"
     */
    static final class HashTrie {
        TrieNode root;
        TrieNode first;

        /**
         * perform Map.get. 
         */
        Object get(int hash, Object key) {
            TrieNode t = root;
            int bit = 1;
            while (t != null && t.hash != hash) {
                t = (hash & bit) == 0? t.left : t.right;
                bit <<= 1;
            }
            for (TrieNode p = t; p != null; p = p.dups) {
                if (p.key == key || (key != null && key.equals(p.key)))
                    return p.value;
            }
            return null;
        }

        /**
         * Perform Map.containsKey
         */
        boolean containsKey(int hash, Object key) {
            TrieNode t = root;
            int bit = 1;
            while (t != null && t.hash != hash) {
                t = (hash & bit) == 0? t.left : t.right;
                bit <<= 1;
            }
            for (TrieNode p = t; p != null; p = p.dups) {
                if (p.key == key || (key != null && key.equals(p.key)))
                    return true;
            }
            return false;
        }


        /**
         * Perform Map.put 
         * @return old value, or ABSENT if added
         */
        Object put(int hash, Object key, Object value) {
            TrieNode t = root;
            TrieNode parent = null;
            int bit = 1;
            int dir = 0;
            while (t != null && t.hash != hash) {
                parent = t;
                t = (dir = (hash & bit)) == 0?  t.left : t.right;
                bit <<= 1;
            }
            for (TrieNode p = t; p != null; p = p.dups) {
                if (p.key == key || (key != null && key.equals(p.key))) 
                    return p.setValue(value);
            }
            TrieNode node = new TrieNode(hash, key, value);
            if (t != null) {
                node.dups = t.dups;
                t.dups = node;
            }
            else if (parent == null)
                root = node;
            else if (dir == 0)
                parent.left = node;
            else
                parent.right = node;
            TrieNode f = first;
            node.next = f;
            if (f != null)
                f.prev = node;
            first = node;
            return ABSENT;
        }

        /**
         * Remove if present
         * @param hash hash for key
         * @param key key
         * @param target if not ABSENT, only remove if key maps to it
         * @return old value, or ABSENT if not found
         */
        Object remove(int hash, Object key, Object target) {
            TrieNode t = root;
            TrieNode parent = null;
            int bit = 1;
            while (t != null && t.hash != hash) {
                parent = t;
                t = ((hash & bit) == 0)? t.left : t.right;
                bit <<= 1;
            }
            TrieNode pred = null;
            TrieNode p = t;
            while (p != null) {
                if (p.key == key || (key != null && key.equals(p.key))) {
                    Object v = p.value;
                    if (target != ABSENT &&
                        (target != v && (target != null || !target.equals(v))))
                        break;
                    unlinkTrieNode(p, parent, pred);
                    return v;
                }
                pred = p;
                p = p.dups;
            }
            return ABSENT;
        }

        /**
         * Unlink given node from traversal list and from trie,
         * replacing its trie links if necessary.  To maintain
         * correspondences of bits and levels, t can only be replaced
         * by a leaf node.  Or if t is already leaf, just unlink it.
         * For sake of iterators, we cannot clear t's next/prev links
         * @param t the node
         * @param parent the node's parent, or null if root
         * @param pred the node's dup list predecessor, or null if none
         */
        void unlinkTrieNode(TrieNode t, TrieNode parent, TrieNode pred) {
            // unlink from traversal list
            TrieNode n = t.next;
            TrieNode p = t.prev;
            if (n != null)
                n.prev = p;
            if (p != null)
                p.next = n;
            else 
                first = n;

            if (pred != null) { 
                // unlink dup node with no tree links
                pred.dups = t.dups; 
                return;
            }
            TrieNode r = t.dups;
            if (r != null) { 
                // replace with element of dup list
                r.left = t.left;
                r.right = t.right;
            }
            else if ((r = t.left != null? t.left : t.right) != null) {
                // find and detach leaf below non-leaf t
                TrieNode rp = t;
                TrieNode c;
                while ((c = r.left != null? r.left : r.right) != null){
                    rp = r;
                    r = c;
                }
                if (r == rp.left)
                    rp.left = null;
                else
                    rp.right = null;
                r.left = t.left;
                r.right = t.right;
            }
            if (parent == null)
                root = r;
            else if (t == parent.left)
                parent.left = r;
            else
                parent.right = r;
        }

        /**
         * Find and unlink a node that would fit in the given table index
         * given the current table mask.  Currently unused.
         */
        TrieNode removeForIndex(int index, int mask) {
            int bit = 1;
            TrieNode t = root;
            TrieNode parent = null;
            while (t != null) {
                if ((t.hash & mask) != index) {
                    parent = t;
                    if ((bit & mask) != 0) {
                        t = ((index & bit) == 0)? t.left : t.right;
                        bit <<= 1;
                    }
                    else // out of signficant bits -- choose any child
                        t = t.left != null? t.left : t.right;
                    continue;
                }
                
                unlinkTrieNode(t, parent, null);
                return t;
            }
            return null;
        }

        /**
         * scan trie nodes for value
         */
        boolean containsValue(Object value) {
            for (TrieNode t = first; t != null; t = t.next) {
                Object v = t.value;
                if (v == value || (value != null && value.equals(v)))
                    return true;
            }
            return false;
        }
    }

}
