/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2005 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution.util;

import java.io.IOException;
import java.util.NoSuchElementException;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.IllegalStateException;
import java.lang.UnsupportedOperationException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.RandomAccess;
import javax.realtime.MemoryArea;
import javolution.context.ObjectFactory;
import javolution.context.PersistentContext;
import javolution.lang.MathLib;
import javolution.lang.Reusable;


/**
 * <p> This class represents a random access collection with real-time behavior 
 *     (smooth capacity increase).</p>
 *     <img src="doc-files/list-add.png"/>
 *     
 * <p> This class has the following advantages over the widely used 
 *     <code>java.util.ArrayList</code>:<ul>
 *     <li> No large array allocation (for large collections multi-dimensional
 *          arrays are employed). The garbage collector is not stressed with
 *          large chunk of memory to allocate (likely to trigger a
 *          full garbage collection due to memory fragmentation).</li>
 *     <li> Support concurrent access/iteration/modification without synchronization
 *          if marked {@link FastCollection#shared shared}. </li>
 *     </ul></p>
 *     
 *  <p> Iterations over the {@link FastTable} values are faster when
 *      performed using the {@link #get} method rather than using collection
 *      records or iterators:[code]
 *     for (int i = 0, n = table.size(); i < n; i++) {
 *          table.get(i);
 *     }[/code]</p>
 *     
 *  <p> {@link FastTable} supports {@link #sort sorting} in place (quick sort) 
 *      using the {@link FastCollection#getValueComparator() value comparator}
 *      for the table (no object or array allocation when sorting).</p>
 * 
 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
 * @version 5.4.5, August 20, 2007
 */
public class FastTable <E> extends FastCollection <E> implements
        List <E> , Reusable, RandomAccess {

    /**
     * Holds the factory for this fast table.
     */
    private static final ObjectFactory FACTORY = new ObjectFactory() {
        public Object create() {
            return new FastTable();
        }
    };

    // We do a full resize (and copy) only when the capacity is less than C1.
    // For large collections, multi-dimensional arrays are employed.

    private static final int B0 = 4; // Initial capacity in bits.

    private static final int C0 = 1 << B0; // Initial capacity (16)

    private static final int B1 = 10; // Low array maximum capacity in bits.

    private static final int C1 = 1 << B1; // Low array maximum capacity (1024).

    private static final int M1 = C1 - 1; // Mask.

    // Resizes up to 1024 maximum (16, 32, 64, 128, 256, 512, 1024). 
    private transient  E [] _low;

    // For larger capacity use multi-dimensional array.
    private transient  E [][] _high;

    /**
     * Holds the current capacity. 
     */
    private transient int _capacity;

    /**
     * Holds the current size. Volatility ensures that when elements are
     * added
     */
    private transient int _size;

    /**
     * Holds the value comparator.
     */
    private transient FastComparator <? super E> _valueComparator = FastComparator.DEFAULT;

    /**
     * Creates a table of small initial capacity.
     */
    public FastTable() {
        _capacity = C0;
        _low = ( E []) new Object[C0];
        _high = ( E [][]) new Object[1][];
        _high[0] = _low;
    }

    /**
     * Creates a persistent table associated to the specified unique identifier
     * (convenience method).
     * 
     * @param id the unique identifier for this map.
     * @throws IllegalArgumentException if the identifier is not unique.
     * @see javolution.context.PersistentContext.Reference
     */
    public FastTable(String id) {
        this();
        new PersistentContext.Reference(id, this) {
            protected void notifyChange() {
                FastTable.this.clear();
                FastTable.this.addAll((FastList) this.get());
            }
        };
    }

    /**
     * Creates a table of specified initial capacity; unless the table size 
     * reaches the specified capacity, operations on this table will not 
     * allocate memory (no lazy object creation).
     * 
     * @param capacity the initial capacity.
     */
    public FastTable(int capacity) {
        this();
        while (capacity > _capacity) {
            increaseCapacity();
        }
    }

    /**
     * Creates a table containing the specified values, in the order they
     * are returned by the collection's iterator.
     *
     * @param values the values to be placed into this table.
     */
    public FastTable(Collection <? extends E> values) {
        this(values.size());
        addAll(values);
    }

    /**
     * Returns a new, preallocated or {@link #recycle recycled} table instance
     * (on the stack when executing in a {@link javolution.context.StackContext
     * StackContext}).
     *
     * @return a new, preallocated or recycled table instance.
     */
    public static <E> FastTable <E> newInstance() {
        return (FastTable <E> ) FACTORY.object();
    }

    /**
     * Recycles a table {@link #newInstance() instance} immediately
     * (on the stack when executing in a {@link javolution.context.StackContext
     * StackContext}). 
     */
    public static void recycle(FastTable instance) {
        FACTORY.recycle(instance);
    }

    /**
     * Sets the size of this table. If the specified size is greater than
     * the current size then <code>null</code> elements are added; otherwise
     * the last elements are removed until the desired size is reached.
     *
     * @param size the new size.
     */
    public void setSize(int size) {
        while (_size < size) { // Adds null elements.
            addLast(null);
        }
        while (_size > size) { // Removes last elements.
            removeLast();
        }
    }

    /**
     * Returns the element at the specified index.
     *
     * @param index index of value to return.
     * @return the value at the specified position in this list.
     * @throws IndexOutOfBoundsException if <code>(index < 0) || 
     *         (index >= size())</code>
     */
    public final  E get(int index) { // Short to be inlined.
        if (index >= _size)
            throw new IndexOutOfBoundsException();
        return index < C1 ? _low[index] : _high[index >> B1][index & M1];
    }

    /**
     * Replaces the value at the specified position in this table with the
     * specified value.
     *
     * @param index index of value to replace.
     * @param value value to be stored at the specified position.
     * @return previous value.
     * @throws IndexOutOfBoundsException if <code>(index < 0) || 
     *         (index >= size())</code>
     */
    public final  E set(int index,  E value) {
        if (index >= _size)
            throw new IndexOutOfBoundsException();
        final  E [] low = _high[index >> B1];
        final  E previous = low[index & M1];
        low[index & M1] = value;
        return previous;
    }

    /**
     * Appends the specified value to the end of this table.
     *
     * @param value the value to be appended to this table.
     * @return <code>true</code> (as per the general contract of the
     *         <code>Collection.add</code> method).
     */
    public final boolean add( E value) {
        if (_size >= _capacity)
            increaseCapacity();
        _high[_size >> B1][_size & M1] = value;
        _size++;
        return true;
    }

    /**
     * Returns the first value of this table.
     *
     * @return this table first value.
     * @throws NoSuchElementException if this table is empty.
     */
    public final  E getFirst() {
        if (_size == 0)
            throw new NoSuchElementException();
        return _low[0];
    }

    /**
     * Returns the last value of this table.
     *
     * @return this table last value.
     * @throws NoSuchElementException if this table is empty.
     */
    public final  E getLast() {
        if (_size == 0)
            throw new NoSuchElementException();
        return get(_size - 1);
    }

    /**
     * Appends the specified value to the end of this table <i>(fast)</i>.
     * 
     * @param value the value to be added.
     */
    public final void addLast( E value) {
        add(value);
    }

    /**
     * Removes and returns the last value of this table <i>(fast)</i>.
     *
     * @return this table's last value before this call.
     * @throws NoSuchElementException if this table is empty.
     */
    public final  E removeLast() {
        if (_size == 0)
            throw new NoSuchElementException();
        _size--; // No need for volatile, removal are not thread-safe.
        final  E [] low = _high[_size >> B1];
        final  E previous = low[_size & M1];
        low[_size & M1] = null;
        return previous;
    }

    // Overrides.
    public final void clear() {
        for (int i = 0; i < _size; i += C1) {
            final int count = MathLib.min(_size - i, C1);
            final  E [] low = _high[i >> B1];
            System.arraycopy(NULL_BLOCK, 0, low, 0, count);
        }
        _size = 0; // No need for volatile, removal are not thread-safe.
    }

    private static final Object[] NULL_BLOCK = (Object[]) new Object[C1];

    // Implements Reusable interface.
    public void reset() {
        clear();
        this.setValueComparator(FastComparator.DEFAULT);
    }

    /**
     * Inserts all of the values in the specified collection into this
     * table at the specified position. Shifts the value currently at that
     * position (if any) and any subsequent values to the right 
     * (increases their indices). 
     * 
     * <p>Note: If this method is used concurrent access must be synchronized
     *          (the table is no more thread-safe).</p>
     *
     * @param index the index at which to insert first value from the specified
     *        collection.
     * @param values the values to be inserted into this list.
     * @return <code>true</code> if this list changed as a result of the call;
     *         <code>false</code> otherwise.
     * @throws IndexOutOfBoundsException if <code>(index < 0) || 
     *         (index > size())</code>
     */
    public final boolean addAll(int index, Collection <? extends E> values) {
        if ((index < 0) || (index > _size))
            throw new IndexOutOfBoundsException("index: " + index);
        final int shift = values.size();
        shiftRight(index, shift);
        Iterator <? extends E> valuesIterator = values.iterator();
        for (int i = index, n = index + shift; i < n; i++) {
            _high[i >> B1][i & M1] = valuesIterator.next();
        }
        _size += shift; // Increases size last (thread-safe)
        return shift != 0;
    }

    /**
     * Inserts the specified value at the specified position in this table.
     * Shifts the value currently at that position
     * (if any) and any subsequent values to the right (adds one to their
     * indices).
     *
     * <p>Note: If this method is used concurrent access must be synchronized
     *          (the table is no more thread-safe).</p>
     *
     * @param index the index at which the specified value is to be inserted.
     * @param value the value to be inserted.
     * @throws IndexOutOfBoundsException if <code>(index < 0) || 
     *         (index > size())</code>
     */
    public final void add(int index,  E value) {
        if ((index < 0) || (index > _size))
            throw new IndexOutOfBoundsException("index: " + index);
        shiftRight(index, 1);
        _high[index >> B1][index & M1] = value;
        _size++;
    }

    /**
     * Removes the value at the specified position from this table.
     * Shifts any subsequent values to the left (subtracts one
     * from their indices). Returns the value that was removed from the
     * table.
     *
     * <p>Note: If this method is used concurrent access must be synchronized
     *          (the table is no more thread-safe).</p>
     *
     * @param index the index of the value to removed.
     * @return the value previously at the specified position.
     * @throws IndexOutOfBoundsException if <code>(index < 0) || 
     *         (index >= size())</code>
     */
    public final  E remove(int index) {
        final  E previous = get(index);
        shiftLeft(index + 1, 1);
        _size--; // No need for volatile, removal are not thread-safe.
        _high[_size >> B1][_size & M1] = null; // Deallocates for GC.
        return previous;
    }

    /**
     * Removes the values between <code>[fromIndex..toIndex[<code> from
     * this table.
     * 
     * <p>Note: If this method is used concurrent access must be synchronized
     *          (the table is no more thread-safe).</p>
     *
     * @param fromIndex the beginning index, inclusive.
     * @param toIndex the ending index, exclusive.
     * @throws IndexOutOfBoundsException if <code>(fromIndex < 0) || (toIndex < 0) 
     *         || (fromIndex > toIndex) || (toIndex > this.size())</code>
     */
       public final void removeRange(int fromIndex, int toIndex) {
        if ((fromIndex < 0) || (toIndex < 0) || (fromIndex > toIndex)
                || (toIndex > _size))
            throw new IndexOutOfBoundsException("FastTable removeRange(" + fromIndex + ", " + toIndex + ") index out of bounds, size: " + _size);
        final int shift = toIndex - fromIndex;
        shiftLeft(toIndex, shift);
        _size -= shift; // No need for volatile, removal are not thread-safe.
        for (int i = _size, n = _size + shift; i < n; i++) {
            _high[i >> B1][i & M1] = null; // Deallocates for GC.
        }
    }

    /**
     * Returns the index in this table of the first occurrence of the specified
     * value, or -1 if this table does not contain this value.
     *
     * @param value the value to search for.
     * @return the index in this table of the first occurrence of the specified
     *         value, or -1 if this table does not contain this value.
     */
    public final int indexOf(Object value) {
        final FastComparator comp = this.getValueComparator();
        for (int i = 0; i < _size;) {
            final  E [] low = _high[i >> B1];
            final int count = MathLib.min(low.length, _size - i);
            for (int j = 0; j < count; j++) {
                if (comp == FastComparator.DEFAULT ? defaultEquals(value,
                        low[j]) : comp.areEqual(value, low[j]))
                    return i + j;
            }
            i += count;
        }
        return -1;
    }

    /**
     * Returns the index in this table of the last occurrence of the specified
     * value, or -1 if this table does not contain this value.
     *
     * @param value the value to search for.
     * @return the index in this table of the last occurrence of the specified
     *         value, or -1 if this table does not contain this value.
     */
    public final int lastIndexOf(Object value) {
        final FastComparator comp = this.getValueComparator();
        for (int i = _size - 1; i >= 0;) {
            final  E [] low = _high[i >> B1];
            final int count =  (i & M1) + 1;
            for (int j = count; --j >= 0;) {
                if (comp == FastComparator.DEFAULT ? defaultEquals(value,
                        low[j]) : comp.areEqual(value, low[j]))
                    return i + j - count + 1;
            }
            i -= count;
        }
        return -1;
    }

    /**
     * Returns an iterator over the elements in this list 
     * (allocated on the stack when executed in a 
     * {@link javolution.context.StackContext StackContext}).
     *
     * @return an iterator over this list values.
     */
    public Iterator <E> iterator() {
        return FastTableIterator.valueOf(this, 0, 0, _size);
    }

    /**
     * Returns a list iterator over the elements in this list 
     * (allocated on the stack when executed in a 
     * {@link javolution.context.StackContext StackContext}).
     *
     * @return an iterator over this list values.
     */
    public ListIterator <E> listIterator() {
        return FastTableIterator.valueOf(this, 0, 0, _size);
    }

    /**
     * Returns a list iterator from the specified position
     * (allocated on the stack when executed in a 
     * {@link javolution.context.StackContext StackContext}).
     * The list iterator being returned does not support insertion/deletion.
     * 
     * @param index the index of first value to be returned from the
     *        list iterator (by a call to the <code>next</code> method).
     * @return a list iterator of the values in this table
     *         starting at the specified position in this list.
     * @throws IndexOutOfBoundsException if the index is out of range 
     *         [code](index < 0 || index > size())[/code]
     */
    public ListIterator <E> listIterator(int index) {
        if ((index < 0) || (index > _size)) throw new IndexOutOfBoundsException();
        return FastTableIterator.valueOf(this, index, 0, _size);
    }

    /**
     * Returns a view of the portion of this list between the specified
     * indexes (instance of {@link FastList} allocated from the "stack" when
     * executing in a {@link javolution.context.StackContext StackContext}).
     * If the specified indexes are equal, the returned list is empty. 
     * The returned list is backed by this list, so non-structural changes in
     * the returned list are reflected in this list, and vice-versa. 
     *
     * This method eliminates the need for explicit range operations (of
     * the sort that commonly exist for arrays). Any operation that expects
     * a list can be used as a range operation by passing a subList view
     * instead of a whole list.  For example, the following idiom
     * removes a range of values from a list: [code]
     * list.subList(from, to).clear();[/code]
     * Similar idioms may be constructed for <code>indexOf</code> and
     * <code>lastIndexOf</code>, and all of the algorithms in the
     * <code>Collections</code> class can be applied to a subList.
     *
     * The semantics of the list returned by this method become undefined if
     * the backing list (i.e., this list) is <i>structurally modified</i> in
     * any way other than via the returned list (structural modifications are
     * those that change the size of this list, or otherwise perturb it in such
     * a fashion that iterations in progress may yield incorrect results).
     *
     * @param fromIndex low endpoint (inclusive) of the subList.
     * @param toIndex high endpoint (exclusive) of the subList.
     * @return a view of the specified range within this list.
     * 
     * @throws IndexOutOfBoundsException if [code](fromIndex < 0 ||
     *          toIndex > size || fromIndex > toIndex)[/code]
     */
    public final List <E> subList(int fromIndex, int toIndex) {
        if ((fromIndex < 0) || (toIndex > _size) || (fromIndex > toIndex))
            throw new IndexOutOfBoundsException("fromIndex: " + fromIndex
                    + ", toIndex: " + toIndex + " for list of size: " + _size);
        return SubTable.valueOf(this, fromIndex, toIndex - fromIndex);
    }

    /**
     * Reduces the capacity of this table to the current size (minimize 
     * storage space).
     */
    public final void trimToSize() {
        while (_capacity - _size > C1) {
            _capacity -= C1;
            _high[_capacity >> B1] = null;
        }
    }

    /**
     * Sorts this table in place (quick sort) using this table 
     * {@link FastCollection#getValueComparator() value comparator}
     * (smallest first).
     * 
     * @return <code>this</code>
     */
    public final FastTable <E> sort() {
        if (_size > 1) {
            quicksort(0, _size - 1, this.getValueComparator());
        }
        return this;
    }

    // From Wikipedia Quick Sort - http://en.wikipedia.org/wiki/Quicksort
    //
    private void quicksort(int first, int last, FastComparator cmp) {
        int pivIndex = 0;
        if (first < last) {
            pivIndex = partition(first, last, cmp);
            quicksort(first, (pivIndex - 1), cmp);
            quicksort((pivIndex + 1), last, cmp);
        }
    }

    private int partition(int f, int l, FastComparator cmp) {
        int up, down;
         E piv = get(f);
        up = f;
        down = l;
        do {
            while (cmp.compare(get(up), piv) <= 0 && up < l) {
                up++;
            }
            while (cmp.compare(get(down), piv) > 0 && down > f) {
                down--;
            }
            if (up < down) { // Swaps.
                 E temp = get(up);
                set(up, get(down));
                set(down, temp);
            }
        } while (down > up);
        set(f, get(down));
        set(down, piv);
        return down;
    }

    /**
     * Sets the comparator to use for value equality or comparison if the 
     * collection is ordered (see {@link #sort()}).
     *
     * @param comparator the value comparator.
     * @return <code>this</code>
     */
    public FastTable <E> setValueComparator(
            FastComparator <? super E> comparator) {
        _valueComparator = comparator;
        return this;
    }

    // Overrides.
    public FastComparator <? super E> getValueComparator() {
        return _valueComparator;
    }

    // Implements FastCollection abstract method.
    public final int size() {
        return _size;
    }

    // Implements FastCollection abstract method.
    public final Record head() {
        return Index.valueOf(-1);
    }

    // Implements FastCollection abstract method.
    public final Record tail() {
        return Index.valueOf(_size);
    }

    // Implements FastCollection abstract method.
    public final  E valueOf(Record record) {
        return get(((Index) record).intValue());
    }

    // Implements FastCollection abstract method.
    public final void delete(Record record) {
        remove(((Index) record).intValue());
    }

    // Overrides  to return a list (JDK1.5+).
    public  List<E> unmodifiable() {
        return ( List<E> ) super.unmodifiable();
    }

    // Overrides (optimization).
    public final boolean contains(Object value) {
        return indexOf(value) >= 0;
    }

    // Requires special handling during de-serialization process.
    private void readObject(ObjectInputStream stream) throws IOException,
            ClassNotFoundException {
        setValueComparator((FastComparator) stream.readObject());
        final int size = stream.readInt();
        _capacity = C0;
        while ((_capacity < _size) && (_capacity < C1)) {
            _capacity <<= 1; // Increases capacity up to C1 to avoid resizes.
        }
        _low = ( E []) new Object[_capacity];
        _high = ( E [][]) new Object[1][];
        _high[0] = _low;
        for (int i = 0; i < size; i++) {
            addLast(( E ) stream.readObject());
        }
    }

    // Requires special handling during serialization process.
    private void writeObject(ObjectOutputStream stream) throws IOException {
        stream.writeObject(getValueComparator());
        final int size = _size;
        stream.writeInt(size);
        for (int i = 0; i < size; i++) {
            stream.writeObject(get(i));
        }
    }

    /**
     * Returns the current capacity of this table.
     *
     * @return this table's capacity.
     */
    protected final int getCapacity() {
        return _capacity;
    }

    /**
     * Increases this table capacity.
     */
    private void increaseCapacity() {
        MemoryArea.getMemoryArea(this).executeInArea(new Runnable() {
            public void run() {
                if (_capacity < C1) { // For small capacity, resize.
                    _capacity <<= 1;
                     E [] tmp = ( E []) new Object[_capacity];
                    System.arraycopy(_low, 0, tmp, 0, _size);
                    _low = tmp;
                    _high[0] = tmp;
                } else { // Add a new low block of 1024 elements.
                    int j = _capacity >> B1;
                    if (j >= _high.length) { // Resizes _high.
                         E [][] tmp = ( E [][]) new Object[_high.length * 2][];
                        System.arraycopy(_high, 0, tmp, 0, _high.length);
                        _high = tmp;
                    }
                    _high[j] = ( E []) new Object[C1];
                    _capacity += C1;
                }
            }
        });
    }

    /**
     * This inner class implements a sub-table.
     */
    private static final class SubTable extends FastCollection implements List,
            RandomAccess {

        private static final ObjectFactory FACTORY = new ObjectFactory() {
            protected Object create() {
                return new SubTable();
            }

            protected void cleanup(Object obj) {
                SubTable st = (SubTable) obj;
                st._table = null;
            }
        };

        private FastTable _table;

        private int _offset;

        private int _size;

        public static SubTable valueOf(FastTable table, int offset, int size) {
            SubTable subTable = (SubTable) FACTORY.object();
            subTable._table = table;
            subTable._offset = offset;
            subTable._size = size;
            return subTable;
        }

        public int size() {
            return _size;
        }

        public Record head() {
            return Index.valueOf(-1);
        }

        public Record tail() {
            return Index.valueOf(_size);
        }

        public Object valueOf(Record record) {
            return _table.get(((Index) record).intValue() + _offset);
        }

        public void delete(Record record) {
            throw new UnsupportedOperationException(
                    "Deletion not supported, thread-safe collections.");
        }

        public boolean addAll(int index, Collection values) {
            throw new UnsupportedOperationException(
                    "Insertion not supported, thread-safe collections.");
        }

        public Object get(int index) {
            if ((index < 0) || (index >= _size))
                throw new IndexOutOfBoundsException("index: " + index);
            return _table.get(index + _offset);
        }

        public Object set(int index, Object value) {
            if ((index < 0) || (index >= _size))
                throw new IndexOutOfBoundsException("index: " + index);
            return _table.set(index + _offset, value);
        }

        public void add(int index, Object element) {
            throw new UnsupportedOperationException(
                    "Insertion not supported, thread-safe collections.");
        }

        public Object remove(int index) {
            throw new UnsupportedOperationException(
                    "Deletion not supported, thread-safe collections.");
        }

        public int indexOf(Object value) {
            final FastComparator comp = _table.getValueComparator();
            for (int i = -1; ++i < _size;) {
                if (comp.areEqual(value, _table.get(i + _offset)))
                    return i;
            }
            return -1;
        }

        public int lastIndexOf(Object value) {
            final FastComparator comp = _table.getValueComparator();
            for (int i = _size; --i >= 0;) {
                if (comp.areEqual(value, _table.get(i + _offset)))
                    return i;
            }
            return -1;
        }

        public ListIterator listIterator() {
            return listIterator(0);
        }

        public ListIterator listIterator(int index) {
            if ((index >= 0) && (index <= _size)) {
                return FastTableIterator.valueOf(_table, index + _offset,
                        _offset, _offset + _size);
            } else {
                throw new IndexOutOfBoundsException("index: " + index
                        + " for table of size: " + _size);
            }
        }

        public List subList(int fromIndex, int toIndex) {
            if ((fromIndex < 0) || (toIndex > _size) || (fromIndex > toIndex))
                throw new IndexOutOfBoundsException("fromIndex: " + fromIndex
                        + ", toIndex: " + toIndex + " for list of size: "
                        + _size);
            return SubTable.valueOf(_table, _offset + fromIndex, toIndex
                    - fromIndex);
        }
    }

    /**
     * This inner class implements a fast table iterator.
     */
    private static final class FastTableIterator implements ListIterator {

        private static final ObjectFactory FACTORY = new ObjectFactory() {
            protected Object create() {
                return new FastTableIterator();
            }

            protected void cleanup(Object obj) {
                FastTableIterator i = (FastTableIterator) obj;
                i._table = null;
                i._low = null;
                i._high = null;
            }
        };

        private FastTable _table;

        private int _currentIndex;

        private int _start; // Inclusive.

        private int _end; // Exclusive.

        private int _nextIndex;

        private Object[] _low;
        
        private Object[][] _high;

        public static FastTableIterator valueOf(FastTable table, int nextIndex,
                int start, int end) {
            FastTableIterator iterator = (FastTableIterator) FACTORY.object();
            iterator._table = table;
            iterator._start = start;
            iterator._end = end;
            iterator._nextIndex = nextIndex;
            iterator._low = table._low;
            iterator._high = table._high;            
            iterator._currentIndex = -1;
            return iterator;
        }

        public boolean hasNext() {
            return (_nextIndex != _end);
        }

        public Object next() {
            if (_nextIndex == _end)
                throw new NoSuchElementException();
            final int i = _currentIndex = _nextIndex++;
            return i < C1 ? _low[i] : _high[i >> B1][i & M1];
        }

        public int nextIndex() {
            return _nextIndex;
        }

        public boolean hasPrevious() {
            return _nextIndex != _start;
        }

        public Object previous() {
            if (_nextIndex == _start)
                throw new NoSuchElementException();
            final int i = _currentIndex = --_nextIndex;
            return i < C1 ? _low[i] : _high[i >> B1][i & M1];
        }

        public int previousIndex() {
            return _nextIndex - 1;
        }

        public void add(Object o) {
            _table.add(_nextIndex++, o);
            _end++;
            _currentIndex = -1;
        }

        public void set(Object o) {
            if (_currentIndex >= 0) {
                _table.set(_currentIndex, o);
            } else {
                throw new IllegalStateException();
            }
        }

        public void remove() {
            if (_currentIndex >= 0) {
                _table.remove(_currentIndex);
                _end--;
                if (_currentIndex < _nextIndex) {
                    _nextIndex--;
                }
                _currentIndex = -1;
            } else {
                throw new IllegalStateException();
            }
        }
    }

    // Shifts element from the specified index to the right (higher indexes). 
    private void shiftRight(int index, int shift) {
        while (_size + shift >= _capacity) {
            increaseCapacity();
        }
        for (int i = _size; --i >= index;) {
            final int dest = i + shift;
            _high[dest >> B1][dest & M1] = _high[i >> B1][i & M1];
        }
    }

    // Shifts element from the specified index to the left (lower indexes). 
    private void shiftLeft(int index, int shift) {
        for (int i = index; i < _size; i++) {
            final int dest = i - shift;
            _high[dest >> B1][dest & M1] = _high[i >> B1][i & M1];
        }
    }

    // For inlining of default comparator. 
    private static boolean defaultEquals(Object o1, Object o2) {
        return (o1 == null) ? (o2 == null) : (o1 == o2) || o1.equals(o2);
    }

    private static final long serialVersionUID = 1L;
}