/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.math.util;

import java.io.Serializable;
import java.util.Arrays;

import org.apache.commons.math.MathRuntimeException;

/**
 * <p>
 * A variable length {@link DoubleArray} implementation that automatically
 * handles expanding and contracting its internal storage array as elements are
 * added and removed.
 * </p>
 * <p>
 * The internal storage array starts with capacity determined by the
 * <code>initialCapacity</code> property, which can be set by the constructor.
 * The default initial capacity is 16. Adding elements using
 * {@link #addElement(double)} appends elements to the end of the array. When
 * there are no open entries at the end of the internal storage array, the array
 * is expanded. The size of the expanded array depends on the
 * <code>expansionMode</code> and <code>expansionFactor</code> properties. The
 * <code>expansionMode</code> determines whether the size of the array is
 * multiplied by the <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
 * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
 * storage locations added). The default <code>expansionMode</code> is
 * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code> is 2.0.
 * </p>
 * <p>
 * The {@link #addElementRolling(double)} method adds a new element to the end
 * of the internal storage array and adjusts the "usable window" of the internal
 * array forward by one position (effectively making what was the second element
 * the first, and so on). Repeated activations of this method (or activation of
 * {@link #discardFrontElements(int)}) will effectively orphan the storage
 * locations at the beginning of the internal storage array. To reclaim this
 * storage, each time one of these methods is activated, the size of the
 * internal storage array is compared to the number of addressable elements (the
 * <code>numElements</code> property) and if the difference is too large, the
 * internal array is contracted to size <code>numElements + 1.</code> The
 * determination of when the internal storage array is "too large" depends on
 * the <code>expansionMode</code> and <code>contractionFactor</code> properties.
 * If the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>,
 * contraction is triggered when the ratio between storage array length and
 * <code>numElements</code> exceeds <code>contractionFactor.</code> If the
 * <code>expansionMode</code> is <code>ADDITIVE_MODE,</code> the number of
 * excess storage locations is compared to <code>contractionFactor.</code>
 * </p>
 * <p>
 * To avoid cycles of expansions and contractions, the
 * <code>expansionFactor</code> must not exceed the
 * <code>contractionFactor.</code> Constructors and mutators for both of these
 * properties enforce this requirement, throwing IllegalArgumentException if it
 * is violated.
 * </p>
 * 
 * @version $Revision$ $Date$
 */
public class ResizableDoubleArray implements DoubleArray, Serializable {

	/** Serializable version identifier */
	private static final long serialVersionUID = -3485529955529426875L;

	/** additive expansion mode */
	public static final int ADDITIVE_MODE = 1;

	/** multiplicative expansion mode */
	public static final int MULTIPLICATIVE_MODE = 0;

	/**
	 * The contraction criteria determines when the internal array will be
	 * contracted to fit the number of elements contained in the element array +
	 * 1.
	 */
	protected float contractionCriteria = 2.5f;

	/**
	 * The expansion factor of the array. When the array needs to be expanded,
	 * the new array size will be
	 * <code>internalArray.length * expansionFactor</code> if
	 * <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, or
	 * <code>internalArray.length + expansionFactor</code> if
	 * <code>expansionMode</code> is set to ADDITIVE_MODE.
	 */
	protected float expansionFactor = 2.0f;

	/**
	 * Determines whether array expansion by <code>expansionFactor</code> is
	 * additive or multiplicative.
	 */
	protected int expansionMode = MULTIPLICATIVE_MODE;

	/**
	 * The initial capacity of the array. Initial capacity is not exposed as a
	 * property as it is only meaningful when passed to a constructor.
	 */
	protected int initialCapacity = 16;

	/**
	 * The internal storage array.
	 */
	protected double[] internalArray;

	/**
	 * The number of addressable elements in the array. Note that this has
	 * nothing to do with the length of the internal storage array.
	 */
	protected int numElements = 0;

	/**
	 * The position of the first addressable element in the internal storage
	 * array. The addressable elements in the array are <code>
	 * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
	 * </code>
	 */
	protected int startIndex = 0;

	/**
	 * Create a ResizableArray with default properties.
	 * <ul>
	 * <li><code>initialCapacity = 16</code></li>
	 * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
	 * <li><code>expansionFactor = 2.5</code></li>
	 * <li><code>contractionFactor = 2.0</code></li>
	 * </ul>
	 */
	public ResizableDoubleArray() {
		internalArray = new double[initialCapacity];
	}

	/**
	 * Create a ResizableArray with the specified initial capacity. Other
	 * properties take default values:
	 * <ul>
	 * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
	 * <li><code>expansionFactor = 2.5</code></li>
	 * <li><code>contractionFactor = 2.0</code></li>
	 * </ul>
	 * 
	 * @param initialCapacity
	 *            The initial size of the internal storage array
	 * @throws IllegalArgumentException
	 *             if initialCapacity is not > 0
	 */
	public ResizableDoubleArray(int initialCapacity) {
		setInitialCapacity(initialCapacity);
		internalArray = new double[this.initialCapacity];
	}

	/**
	 * <p>
	 * Create a ResizableArray with the specified initial capacity and expansion
	 * factor. The remaining properties take default values:
	 * <ul>
	 * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
	 * <li><code>contractionFactor = 0.5 + expansionFactor</code></li>
	 * </ul>
	 * </p>
	 * <p>
	 * Throws IllegalArgumentException if the following conditions are not met:
	 * <ul>
	 * <li><code>initialCapacity > 0</code></li>
	 * <li><code>expansionFactor > 1</code></li>
	 * </ul>
	 * </p>
	 * 
	 * @param initialCapacity
	 *            The initial size of the internal storage array
	 * @param expansionFactor
	 *            the array will be expanded based on this parameter
	 * @throws IllegalArgumentException
	 *             if parameters are not valid
	 */
	public ResizableDoubleArray(int initialCapacity, float expansionFactor) {
		this.expansionFactor = expansionFactor;
		setInitialCapacity(initialCapacity);
		internalArray = new double[initialCapacity];
		setContractionCriteria(expansionFactor + 0.5f);
	}

	/**
	 * <p>
	 * Create a ResizableArray with the specified initialCapacity,
	 * expansionFactor, and contractionCriteria. The <code>expansionMode</code>
	 * will default to <code>MULTIPLICATIVE_MODE.</code>
	 * </p>
	 * <p>
	 * Throws IllegalArgumentException if the following conditions are not met:
	 * <ul>
	 * <li><code>initialCapacity > 0</code></li>
	 * <li><code>expansionFactor > 1</code></li>
	 * <li><code>contractionFactor >= expansionFactor</code></li>
	 * </ul>
	 * </p>
	 * 
	 * @param initialCapacity
	 *            The initial size of the internal storage array
	 * @param expansionFactor
	 *            the array will be expanded based on this parameter
	 * @param contractionCriteria
	 *            The contraction Criteria.
	 * @throws IllegalArgumentException
	 *             if parameters are not valid
	 */
	public ResizableDoubleArray(int initialCapacity, float expansionFactor, float contractionCriteria) {
		this.expansionFactor = expansionFactor;
		setContractionCriteria(contractionCriteria);
		setInitialCapacity(initialCapacity);
		internalArray = new double[initialCapacity];
	}

	/**
	 * <p>
	 * Create a ResizableArray with the specified properties.
	 * </p>
	 * <p>
	 * Throws IllegalArgumentException if the following conditions are not met:
	 * <ul>
	 * <li><code>initialCapacity > 0</code></li>
	 * <li><code>expansionFactor > 1</code></li>
	 * <li><code>contractionFactor >= expansionFactor</code></li>
	 * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code>
	 * </li>
	 * </ul>
	 * </p>
	 * 
	 * @param initialCapacity
	 *            the initial size of the internal storage array
	 * @param expansionFactor
	 *            the array will be expanded based on this parameter
	 * @param contractionCriteria
	 *            the contraction Criteria
	 * @param expansionMode
	 *            the expansion mode
	 * @throws IllegalArgumentException
	 *             if parameters are not valid
	 */
	public ResizableDoubleArray(int initialCapacity, float expansionFactor, float contractionCriteria,
			int expansionMode) {
		this.expansionFactor = expansionFactor;
		setContractionCriteria(contractionCriteria);
		setInitialCapacity(initialCapacity);
		setExpansionMode(expansionMode);
		internalArray = new double[initialCapacity];
	}

	/**
	 * Copy constructor. Creates a new ResizableDoubleArray that is a deep,
	 * fresh copy of the original. Needs to acquire synchronization lock on
	 * original. Original may not be null; otherwise a NullPointerException is
	 * thrown.
	 * 
	 * @param original
	 *            array to copy
	 * @since 2.0
	 */
	public ResizableDoubleArray(ResizableDoubleArray original) {
		copy(original, this);
	}

	/**
	 * Adds an element to the end of this expandable array.
	 * 
	 * @param value
	 *            to be added to end of array
	 */
	public synchronized void addElement(double value) {
		numElements++;
		if ((startIndex + numElements) > internalArray.length) {
			expand();
		}
		internalArray[startIndex + (numElements - 1)] = value;
		if (shouldContract()) {
			contract();
		}
	}

	/**
	 * <p>
	 * Adds an element to the end of the array and removes the first element in
	 * the array. Returns the discarded first element. The effect is similar to
	 * a push operation in a FIFO queue.
	 * </p>
	 * <p>
	 * Example: If the array contains the elements 1, 2, 3, 4 (in that order)
	 * and addElementRolling(5) is invoked, the result is an array containing
	 * the entries 2, 3, 4, 5 and the value returned is 1.
	 * </p>
	 * 
	 * @param value
	 *            the value to be added to the array
	 * @return the value which has been discarded or "pushed" out of the array
	 *         by this rolling insert
	 */
	public synchronized double addElementRolling(double value) {
		double discarded = internalArray[startIndex];

		if ((startIndex + (numElements + 1)) > internalArray.length) {
			expand();
		}
		// Increment the start index
		startIndex += 1;

		// Add the new value
		internalArray[startIndex + (numElements - 1)] = value;

		// Check the contraction criteria
		if (shouldContract()) {
			contract();
		}
		return discarded;
	}

	/**
	 * Substitutes <code>value</code> for the most recently added value. Returns
	 * the value that has been replaced. If the array is empty (i.e. if
	 * {@link #numElements} is zero), a MathRuntimeException is thrown.
	 * 
	 * @param value
	 *            new value to substitute for the most recently added value
	 * @return value that has been replaced in the array
	 * @since 2.0
	 */
	public synchronized double substituteMostRecentElement(double value) {
		if (numElements < 1) {
			throw MathRuntimeException
					.createArrayIndexOutOfBoundsException("cannot substitute an element from an empty array");
		}

		double discarded = internalArray[startIndex + (numElements - 1)];

		internalArray[startIndex + (numElements - 1)] = value;

		return discarded;
	}

	/**
	 * Checks the expansion factor and the contraction criteria and throws an
	 * IllegalArgumentException if the contractionCriteria is less than the
	 * expansionCriteria
	 * 
	 * @param expansionFactor
	 *            factor to be checked
	 * @param contractionCriteria
	 *            criteria to be checked
	 * @throws IllegalArgumentException
	 *             if the contractionCriteria is less than the
	 *             expansionCriteria.
	 */
	protected void checkContractExpand(float contractionCriteria, float expansionFactor) {

		if (contractionCriteria < expansionFactor) {
			throw MathRuntimeException.createIllegalArgumentException(
					"contraction criteria ({0}) smaller than the expansion factor ({1}).  This would "
							+ "lead to a never ending loop of expansion and contraction as a newly expanded "
							+ "internal storage array would immediately satisfy the criteria for contraction",
					contractionCriteria, expansionFactor);
		}

		if (contractionCriteria <= 1.0) {
			throw MathRuntimeException
					.createIllegalArgumentException(
							"contraction criteria smaller than one ({0}).  This would lead to a never ending "
									+ "loop of expansion and contraction as an internal storage array length equal "
									+ "to the number of elements would satisfy the contraction criteria.",
							contractionCriteria);
		}

		if (expansionFactor <= 1.0) {
			throw MathRuntimeException.createIllegalArgumentException("expansion factor smaller than one ({0})",
					expansionFactor);
		}
	}

	/**
	 * Clear the array, reset the size to the initialCapacity and the number of
	 * elements to zero.
	 */
	public synchronized void clear() {
		numElements = 0;
		internalArray = new double[initialCapacity];
	}

	/**
	 * Contracts the storage array to the (size of the element set) + 1 - to
	 * avoid a zero length array. This function also resets the startIndex to
	 * zero.
	 */
	public synchronized void contract() {
		double[] tempArray = new double[numElements + 1];

		// Copy and swap - copy only the element array from the src array.
		System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
		internalArray = tempArray;

		// Reset the start index to zero
		startIndex = 0;
	}

	/**
	 * Discards the <code>i<code> initial elements of the array.  For example,
	 * if the array contains the elements 1,2,3,4, invoking 
	 * <code>discardFrontElements(2)</code> will cause the first two elements to
	 * be discarded, leaving 3,4 in the array. Throws illegalArgumentException
	 * if i exceeds numElements.
	 * 
	 * @param i
	 *            the number of elements to discard from the front of the array
	 * @throws IllegalArgumentException
	 *             if i is greater than numElements.
	 * @since 2.0
	 */
	public synchronized void discardFrontElements(int i) {

		discardExtremeElements(i, true);

	}

	/**
	 * Discards the <code>i<code> last elements of the array.  For example,
	 * if the array contains the elements 1,2,3,4, invoking 
	 * <code>discardMostRecentElements(2)</code> will cause the last two
	 * elements to be discarded, leaving 1,2 in the array. Throws
	 * illegalArgumentException if i exceeds numElements.
	 * 
	 * @param i
	 *            the number of elements to discard from the end of the array
	 * @throws IllegalArgumentException
	 *             if i is greater than numElements.
	 * @since 2.0
	 */
	public synchronized void discardMostRecentElements(int i) {

		discardExtremeElements(i, false);

	}

	/**
	 * Discards the <code>i<code> first or last elements of the array,
	 * depending on the value of <code>front</code>. For example, if the array
	 * contains the elements 1,2,3,4, invoking
	 * <code>discardExtremeElements(2,false)</code> will cause the last two
	 * elements to be discarded, leaving 1,2 in the array. For example, if the
	 * array contains the elements 1,2,3,4, invoking
	 * <code>discardExtremeElements(2,true)</code> will cause the first two
	 * elements to be discarded, leaving 3,4 in the array. Throws
	 * illegalArgumentException if i exceeds numElements.
	 * 
	 * @param i
	 *            the number of elements to discard from the front/end of the
	 *            array
	 * @param front
	 *            true if elements are to be discarded from the front of the
	 *            array, false if elements are to be discarded from the end of
	 *            the array
	 * @throws IllegalArgumentException
	 *             if i is greater than numElements.
	 * @since 2.0
	 */
	private synchronized void discardExtremeElements(int i, boolean front) {
		if (i > numElements) {
			throw MathRuntimeException.createIllegalArgumentException(
					"cannot discard {0} elements from a {1} elements array", i, numElements);
		} else if (i < 0) {
			throw MathRuntimeException
					.createIllegalArgumentException("cannot discard a negative number of elements ({0})", i);
		} else {
			// "Subtract" this number of discarded from numElements
			numElements -= i;
			if (front)
				startIndex += i;
		}
		if (shouldContract()) {
			contract();
		}
	}

	/**
	 * Expands the internal storage array using the expansion factor.
	 * <p>
	 * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, the new
	 * array size will be <code>internalArray.length * expansionFactor.</code>
	 * If <code>expansionMode</code> is set to ADDITIVE_MODE, the length after
	 * expansion will be <code>internalArray.length + expansionFactor</code>
	 * </p>
	 */
	protected synchronized void expand() {

		// notice the use of Math.ceil(), this guarantees that we will always
		// have an array of at least currentSize + 1. Assume that the
		// current initial capacity is 1 and the expansion factor
		// is 1.000000000000000001. The newly calculated size will be
		// rounded up to 2 after the multiplication is performed.
		int newSize = 0;
		if (expansionMode == MULTIPLICATIVE_MODE) {
			newSize = (int) Math.ceil(internalArray.length * expansionFactor);
		} else {
			newSize = internalArray.length + Math.round(expansionFactor);
		}
		double[] tempArray = new double[newSize];

		// Copy and swap
		System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
		internalArray = tempArray;
	}

	/**
	 * Expands the internal storage array to the specified size.
	 * 
	 * @param size
	 *            Size of the new internal storage array
	 */
	private synchronized void expandTo(int size) {
		double[] tempArray = new double[size];
		// Copy and swap
		System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
		internalArray = tempArray;
	}

	/**
	 * The contraction criteria defines when the internal array will contract to
	 * store only the number of elements in the element array. If the
	 * <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>,
	 * contraction is triggered when the ratio between storage array length and
	 * <code>numElements</code> exceeds <code>contractionFactor</code>. If the
	 * <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the number of
	 * excess storage locations is compared to <code>contractionFactor.</code>
	 * 
	 * @return the contraction criteria used to reclaim memory.
	 */
	public float getContractionCriteria() {
		return contractionCriteria;
	}

	/**
	 * Returns the element at the specified index
	 * 
	 * @param index
	 *            index to fetch a value from
	 * @return value stored at the specified index
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is less than zero or is greater than
	 *             <code>getNumElements() - 1</code>.
	 */
	public synchronized double getElement(int index) {
		if (index >= numElements) {
			throw MathRuntimeException.createArrayIndexOutOfBoundsException(
					"the index specified: {0} is larger than the current maximal index {1}", index, numElements - 1);
		} else if (index >= 0) {
			return internalArray[startIndex + index];
		} else {
			throw MathRuntimeException.createArrayIndexOutOfBoundsException(
					"elements cannot be retrieved from a negative array index {0}", index);
		}
	}

	/**
	 * Returns a double array containing the elements of this
	 * <code>ResizableArray</code>. This method returns a copy, not a reference
	 * to the underlying array, so that changes made to the returned array have
	 * no effect on this <code>ResizableArray.</code>
	 * 
	 * @return the double array.
	 */
	public synchronized double[] getElements() {
		double[] elementArray = new double[numElements];
		System.arraycopy(internalArray, startIndex, elementArray, 0, numElements);
		return elementArray;
	}

	/**
	 * The expansion factor controls the size of a new array when an array needs
	 * to be expanded. The <code>expansionMode</code> determines whether the
	 * size of the array is multiplied by the <code>expansionFactor</code>
	 * (MULTIPLICATIVE_MODE) or if the expansion is additive (ADDITIVE_MODE --
	 * <code>expansionFactor</code> storage locations added). The default
	 * <code>expansionMode</code> is MULTIPLICATIVE_MODE and the default
	 * <code>expansionFactor</code> is 2.0.
	 * 
	 * @return the expansion factor of this expandable double array
	 */
	public float getExpansionFactor() {
		return expansionFactor;
	}

	/**
	 * The <code>expansionMode</code> determines whether the internal storage
	 * array grows additively (ADDITIVE_MODE) or multiplicatively
	 * (MULTIPLICATIVE_MODE) when it is expanded.
	 * 
	 * @return Returns the expansionMode.
	 */
	public int getExpansionMode() {
		return expansionMode;
	}

	/**
	 * Notice the package scope on this method. This method is simply here for
	 * the JUnit test, it allows us check if the expansion is working properly
	 * after a number of expansions. This is not meant to be a part of the
	 * public interface of this class.
	 * 
	 * @return the length of the internal storage array.
	 */
	synchronized int getInternalLength() {
		return (internalArray.length);
	}

	/**
	 * Returns the number of elements currently in the array. Please note that
	 * this is different from the length of the internal storage array.
	 *
	 * @return number of elements
	 */
	public synchronized int getNumElements() {
		return (numElements);
	}

	/**
	 * Returns the internal storage array. Note that this method returns a
	 * reference to the internal storage array, not a copy, and to correctly
	 * address elements of the array, the <code>startIndex</code> is required
	 * (available via the {@link #start} method). This method should only be
	 * used in cases where copying the internal array is not practical. The
	 * {@link #getElements} method should be used in all other cases.
	 *
	 * 
	 * @return the internal storage array used by this object
	 * @deprecated replaced by {@link #getInternalValues()} as of 2.0
	 */
	@Deprecated
	public synchronized double[] getValues() {
		return (internalArray);
	}

	/**
	 * Returns the internal storage array. Note that this method returns a
	 * reference to the internal storage array, not a copy, and to correctly
	 * address elements of the array, the <code>startIndex</code> is required
	 * (available via the {@link #start} method). This method should only be
	 * used in cases where copying the internal array is not practical. The
	 * {@link #getElements} method should be used in all other cases.
	 *
	 * 
	 * @return the internal storage array used by this object
	 * @since 2.0
	 */
	public synchronized double[] getInternalValues() {
		return (internalArray);
	}

	/**
	 * Sets the contraction criteria for this ExpandContractDoubleArray.
	 * 
	 * @param contractionCriteria
	 *            contraction criteria
	 */
	public void setContractionCriteria(float contractionCriteria) {
		checkContractExpand(contractionCriteria, getExpansionFactor());
		synchronized (this) {
			this.contractionCriteria = contractionCriteria;
		}
	}

	/**
	 * Sets the element at the specified index. If the specified index is
	 * greater than <code>getNumElements() - 1</code>, the
	 * <code>numElements</code> property is increased to <code>index +1</code>
	 * and additional storage is allocated (if necessary) for the new element
	 * and all (uninitialized) elements between the new element and the previous
	 * end of the array).
	 * 
	 * @param index
	 *            index to store a value in
	 * @param value
	 *            value to store at the specified index
	 * @throws ArrayIndexOutOfBoundsException
	 *             if <code>index</code> is less than zero.
	 */
	public synchronized void setElement(int index, double value) {
		if (index < 0) {
			throw MathRuntimeException
					.createArrayIndexOutOfBoundsException("cannot set an element at a negative index {0}", index);
		}
		if (index + 1 > numElements) {
			numElements = index + 1;
		}
		if ((startIndex + index) >= internalArray.length) {
			expandTo(startIndex + (index + 1));
		}
		internalArray[startIndex + index] = value;
	}

	/**
	 * Sets the expansionFactor. Throws IllegalArgumentException if the the
	 * following conditions are not met:
	 * <ul>
	 * <li><code>expansionFactor > 1</code></li>
	 * <li><code>contractionFactor >= expansionFactor</code></li>
	 * </ul>
	 * 
	 * @param expansionFactor
	 *            the new expansion factor value.
	 * @throws IllegalArgumentException
	 *             if expansionFactor is <= 1 or greater than contractionFactor
	 */
	public void setExpansionFactor(float expansionFactor) {
		checkContractExpand(getContractionCriteria(), expansionFactor);
		// The check above verifies that the expansion factor is > 1.0;
		synchronized (this) {
			this.expansionFactor = expansionFactor;
		}
	}

	/**
	 * Sets the <code>expansionMode</code>. The specified value must be one of
	 * ADDITIVE_MODE, MULTIPLICATIVE_MODE.
	 * 
	 * @param expansionMode
	 *            The expansionMode to set.
	 * @throws IllegalArgumentException
	 *             if the specified mode value is not valid
	 */
	public void setExpansionMode(int expansionMode) {
		if (expansionMode != MULTIPLICATIVE_MODE && expansionMode != ADDITIVE_MODE) {
			throw MathRuntimeException.createIllegalArgumentException(
					"unsupported expansion mode {0}, supported modes are {1} ({2}) and {3} ({4})", expansionMode,
					MULTIPLICATIVE_MODE, "MULTIPLICATIVE_MODE", ADDITIVE_MODE, "ADDITIVE_MODE");
		}
		synchronized (this) {
			this.expansionMode = expansionMode;
		}
	}

	/**
	 * Sets the initial capacity. Should only be invoked by constructors.
	 * 
	 * @param initialCapacity
	 *            of the array
	 * @throws IllegalArgumentException
	 *             if <code>initialCapacity</code> is not positive.
	 */
	protected void setInitialCapacity(int initialCapacity) {
		if (initialCapacity > 0) {
			synchronized (this) {
				this.initialCapacity = initialCapacity;
			}
		} else {
			throw MathRuntimeException.createIllegalArgumentException("initial capacity ({0}) is not positive",
					initialCapacity);
		}
	}

	/**
	 * This function allows you to control the number of elements contained in
	 * this array, and can be used to "throw out" the last n values in an array.
	 * This function will also expand the internal array as needed.
	 * 
	 * @param i
	 *            a new number of elements
	 * @throws IllegalArgumentException
	 *             if <code>i</code> is negative.
	 */
	public synchronized void setNumElements(int i) {

		// If index is negative thrown an error
		if (i < 0) {
			throw MathRuntimeException.createIllegalArgumentException("index ({0}) is not positive", i);
		}

		// Test the new num elements, check to see if the array needs to be
		// expanded to accommodate this new number of elements
		if ((startIndex + i) > internalArray.length) {
			expandTo(startIndex + i);
		}

		// Set the new number of elements to new value
		numElements = i;
	}

	/**
	 * Returns true if the internal storage array has too many unused storage
	 * positions.
	 * 
	 * @return true if array satisfies the contraction criteria
	 */
	private synchronized boolean shouldContract() {
		if (expansionMode == MULTIPLICATIVE_MODE) {
			return (internalArray.length / ((float) numElements)) > contractionCriteria;
		} else {
			return (internalArray.length - numElements) > contractionCriteria;
		}
	}

	/**
	 * Returns the starting index of the internal array. The starting index is
	 * the position of the first addressable element in the internal storage
	 * array. The addressable elements in the array are <code>
	 * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
	 * </code>
	 *
	 * @return starting index
	 */
	public synchronized int start() {
		return startIndex;
	}

	/**
	 * <p>
	 * Copies source to dest, copying the underlying data, so dest is a new,
	 * independent copy of source. Does not contract before the copy.
	 * </p>
	 * 
	 * <p>
	 * Obtains synchronization locks on both source and dest (in that order)
	 * before performing the copy.
	 * </p>
	 * 
	 * <p>
	 * Neither source nor dest may be null; otherwise a NullPointerException is
	 * thrown
	 * </p>
	 * 
	 * @param source
	 *            ResizableDoubleArray to copy
	 * @param dest
	 *            ResizableArray to replace with a copy of the source array
	 * @since 2.0
	 * 
	 */
	public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest) {
		synchronized (source) {
			synchronized (dest) {
				dest.initialCapacity = source.initialCapacity;
				dest.contractionCriteria = source.contractionCriteria;
				dest.expansionFactor = source.expansionFactor;
				dest.expansionMode = source.expansionMode;
				dest.internalArray = new double[source.internalArray.length];
				System.arraycopy(source.internalArray, 0, dest.internalArray, 0, dest.internalArray.length);
				dest.numElements = source.numElements;
				dest.startIndex = source.startIndex;
			}
		}
	}

	/**
	 * Returns a copy of the ResizableDoubleArray. Does not contract before the
	 * copy, so the returned object is an exact copy of this.
	 * 
	 * @return a new ResizableDoubleArray with the same data and configuration
	 *         properties as this
	 * @since 2.0
	 */
	public synchronized ResizableDoubleArray copy() {
		ResizableDoubleArray result = new ResizableDoubleArray();
		copy(this, result);
		return result;
	}

	/**
	 * Returns true iff object is a ResizableDoubleArray with the same
	 * properties as this and an identical internal storage array.
	 * 
	 * @param object
	 *            object to be compared for equality with this
	 * @return true iff object is a ResizableDoubleArray with the same data and
	 *         properties as this
	 * @since 2.0
	 */
	@Override
	public boolean equals(Object object) {
		if (object == this) {
			return true;
		}
		if (object instanceof ResizableDoubleArray == false) {
			return false;
		}
		synchronized (this) {
			synchronized (object) {
				boolean result = true;
				ResizableDoubleArray other = (ResizableDoubleArray) object;
				result = result && (other.initialCapacity == initialCapacity);
				result = result && (other.contractionCriteria == contractionCriteria);
				result = result && (other.expansionFactor == expansionFactor);
				result = result && (other.expansionMode == expansionMode);
				result = result && (other.numElements == numElements);
				result = result && (other.startIndex == startIndex);
				if (!result) {
					return false;
				} else {
					return Arrays.equals(internalArray, other.internalArray);
				}
			}
		}
	}

	/**
	 * Returns a hash code consistent with equals.
	 * 
	 * @return hash code representing this ResizableDoubleArray
	 * @since 2.0
	 */
	@Override
	public synchronized int hashCode() {
		int[] hashData = new int[7];
		hashData[0] = new Float(expansionFactor).hashCode();
		hashData[1] = new Float(contractionCriteria).hashCode();
		hashData[2] = expansionMode;
		hashData[3] = Arrays.hashCode(internalArray);
		hashData[4] = initialCapacity;
		hashData[5] = numElements;
		hashData[6] = startIndex;
		return Arrays.hashCode(hashData);
	}

}
