@SuppressWarnings("unchecked")
	@Override public
    T remove(int index) {
        /* Grab the value of the element we're removing; this also verifies
         * the index.
         */
        T result = get(index);

        /* Removing an element from the array is tricky because we have to
         * maintain the invariant that there are an even number of elements
         * in the new array and that they are in the central elements.
         * There are two cases we need to consider.
         * 
         * 1. The element to remove is in the new array.  We need to remove
         *    two elements from this array, one that actually got deleted
         *    and one that needs to be moved back to the old array.  We'll
         *    do a standard shuffle-down algorithm to cover up the element
         *    that was deleted, then we'll move the oldest element down.
         * 2. The element to remove is in the old array.  We need to move
         *    two elements from the new array down into the old one.  To
         *    do this, we'll do the standard array shuffle-down algorithm
         *    on the old array, then will copy the first two elements of
         *    the new array into the old.  Finally, we shuffle the remaining
         *    elements of the new array down one spot.
         *
         * After doing this step, we'll need to determine whether or not
         * to do a rebalance by tossing out the new list and swapping the
         * old list forward.
         */
        if (index + 1 > mShadow) { // In the new array
            /* Scoot the elements past this element down on top of the
             * element itself.  The number of elements to copy is given
             * by the number of elements between mEnd - 1 (the last element)
             * and index + 1 (the element to delete)
             */
            System.arraycopy(mNew, index + 2, mNew, index + 1,
                             (mEnd - 1) - (index + 1));

            /* Move the element before the shadow down. */
            mOld[mShadow + 1] = mNew[mShadow + 1];

            /* To be nice to the garbage collector, clear the endpoints. */
            mNew[mEnd - 1] = null;
            mNew[mShadow + 1] = null;

            /* Pull the shadow and endpoint closer together. */
            ++mShadow; --mEnd;
        } else { // In old array
            /* Shuffle down the elements in the old array to cover the
             * element that was removed.  The math is similar to above.
             */
            System.arraycopy(mOld, index + 2, mOld, index + 1,
                             mShadow - (index + 1));

            /* Move the last two elements of the new array into the old one.
             * Recall that mShadow points to the last unshadowed element in
             * the old array, and so we'll copy from one spot past there
             * in the new array.
             */
            for (int i = 0; i < 2; ++i) {
                mOld[mShadow + i] = mNew[mShadow + 1 + i];
                mNew[mShadow + 1 + i] = null; // Place nice with the GC
            }

            /* The new shadow point is one step past where it once was,
             * since we just pulled two elements down, but lost an
             * old element.
             */
            ++mShadow;

            /* Shuffle the elements of the new array down one position.
             * The beginning position is one past the shadow, as it always
             * is.
             */
            System.arraycopy(mNew, mShadow + 2, mNew, mShadow + 1,
                             mEnd - mShadow - 1);

            /* Clear the element that just got moved. */
            mNew[mEnd - 1] = null;

            /* Back up the end position, since we just lost an element. */
            --mEnd;
        }

        /* Finally, see if we just emptied the new array.  This happens if
         * the end pointer is one step past the shadow pointer.
         */
        if (mEnd == mShadow + 1) {
            /* Drop the new array and promote the old array to new. */
            mNew = mOld;

            /* Make a blank array for new elements. */
            mOld = (T[]) new Object[mNew.length / 2];

            /* Move the end and shadow pointers off the ends of the array. */
            mShadow = -1;
            mEnd = mNew.length;
        }

        return result;
    }