    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.math.util;
    018
    019    import java.io.Serializable;
    020    import java.util.Arrays;
    021
    022    import org.apache.commons.math.MathRuntimeException;
    023    import org.apache.commons.math.exception.util.LocalizedFormats;
    024
    025    /**
    026     * <p>
    027     * A variable length {@link DoubleArray} implementation that automatically
    028     * handles expanding and contracting its internal storage array as elements
    029     * are added and removed.
    030     * </p>
    031     * <p>
    032     *  The internal storage array starts with capacity determined by the
    033     * <code>initialCapacity</code> property, which can be set by the constructor.
    034     * The default initial capacity is 16.  Adding elements using
    035     * {@link #addElement(double)} appends elements to the end of the array.  When
    036     * there are no open entries at the end of the internal storage array, the
    037     * array is expanded.  The size of the expanded array depends on the
    038     * <code>expansionMode</code> and <code>expansionFactor</code> properties.
    039     * The <code>expansionMode</code> determines whether the size of the array is
    040     * multiplied by the <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
    041     * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
    042     * storage locations added).  The default <code>expansionMode</code> is
    043     * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
    044     * is 2.0.
    045     * </p>
    046     * <p>
    047     * The {@link #addElementRolling(double)} method adds a new element to the end
    048     * of the internal storage array and adjusts the "usable window" of the
    049     * internal array forward by one position (effectively making what was the
    050     * second element the first, and so on).  Repeated activations of this method
    051     * (or activation of {@link #discardFrontElements(int)}) will effectively orphan
    052     * the storage locations at the beginning of the internal storage array.  To
    053     * reclaim this storage, each time one of these methods is activated, the size
    054     * of the internal storage array is compared to the number of addressable
    055     * elements (the <code>numElements</code> property) and if the difference
    056     * is too large, the internal array is contracted to size
    057     * <code>numElements + 1.</code>  The determination of when the internal
    058     * storage array is "too large" depends on the <code>expansionMode</code> and
    059     * <code>contractionFactor</code> properties.  If  the <code>expansionMode</code>
    060     * is <code>MULTIPLICATIVE_MODE</code>, contraction is triggered when the
    061     * ratio between storage array length and <code>numElements</code> exceeds
    062     * <code>contractionFactor.</code>  If the <code>expansionMode</code>
    063     * is <code>ADDITIVE_MODE,</code> the number of excess storage locations
    064     * is compared to <code>contractionFactor.</code>
    065     * </p>
    066     * <p>
    067     * To avoid cycles of expansions and contractions, the
    068     * <code>expansionFactor</code> must not exceed the
    069     * <code>contractionFactor.</code> Constructors and mutators for both of these
    070     * properties enforce this requirement, throwing IllegalArgumentException if it
    071     * is violated.
    072     * </p>
    073     * @version $Revision: 1073474 $ $Date: 2011-02-22 20:52:17 +0100 (mar. 22 f??vr. 2011) $
    074     */
    075    public class ResizableDoubleArray implements DoubleArray, Serializable {
    076
    077        /** additive expansion mode */
    078        public static final int ADDITIVE_MODE = 1;
    079
    080        /** multiplicative expansion mode */
    081        public static final int MULTIPLICATIVE_MODE = 0;
    082
    083        /** Serializable version identifier */
    084        private static final long serialVersionUID = -3485529955529426875L;
    085
    086        /**
    087         * The contraction criteria determines when the internal array will be
    088         * contracted to fit the number of elements contained in the element
    089         *  array + 1.
    090         */
    091        protected float contractionCriteria = 2.5f;
    092
    093        /**
    094         * The expansion factor of the array.  When the array needs to be expanded,
    095         * the new array size will be
    096         * <code>internalArray.length * expansionFactor</code>
    097         * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, or
    098         * <code>internalArray.length + expansionFactor</code> if
    099         * <code>expansionMode</code> is set to ADDITIVE_MODE.
    100         */
    101        protected float expansionFactor = 2.0f;
    102
    103        /**
    104         * Determines whether array expansion by <code>expansionFactor</code>
    105         * is additive or multiplicative.
    106         */
    107        protected int expansionMode = MULTIPLICATIVE_MODE;
    108
    109        /**
    110         * The initial capacity of the array.  Initial capacity is not exposed as a
    111         * property as it is only meaningful when passed to a constructor.
    112         */
    113        protected int initialCapacity = 16;
    114
    115        /**
    116         * The internal storage array.
    117         */
    118        protected double[] internalArray;
    119
    120        /**
    121         * The number of addressable elements in the array.  Note that this
    122         * has nothing to do with the length of the internal storage array.
    123         */
    124        protected int numElements = 0;
    125
    126        /**
    127         * The position of the first addressable element in the internal storage
    128         * array.  The addressable elements in the array are <code>
    129         * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
    130         * </code>
    131         */
    132        protected int startIndex = 0;
    133
    134        /**
    135         * Create a ResizableArray with default properties.
    136         * <ul>
    137         * <li><code>initialCapacity = 16</code></li>
    138         * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
    139         * <li><code>expansionFactor = 2.5</code></li>
    140         * <li><code>contractionFactor = 2.0</code></li>
    141         * </ul>
    142         */
    143        public ResizableDoubleArray() {
    144            internalArray = new double[initialCapacity];
    145        }
    146
    147        /**
    148         * Create a ResizableArray with the specified initial capacity.  Other
    149         * properties take default values:
    150          * <ul>
    151         * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
    152         * <li><code>expansionFactor = 2.5</code></li>
    153         * <li><code>contractionFactor = 2.0</code></li>
    154         * </ul>
    155         * @param initialCapacity The initial size of the internal storage array
    156         * @throws IllegalArgumentException if initialCapacity is not > 0
    157         */
    158        public ResizableDoubleArray(int initialCapacity) {
    159            setInitialCapacity(initialCapacity);
    160            internalArray = new double[this.initialCapacity];
    161        }
    162
    163        /**
    164         * Create a ResizableArray from an existing double[] with the
    165         * initial capacity and numElements corresponding to the size of
    166         * the supplied double[] array. If the supplied array is null, a
    167         * new empty array with the default initial capacity will be created.
    168         * The input array is copied, not referenced.
    169         * Other properties take default values:
    170         * <ul>
    171         * <li><code>initialCapacity = 16</code></li>
    172         * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
    173         * <li><code>expansionFactor = 2.5</code></li>
    174         * <li><code>contractionFactor = 2.0</code></li>
    175         * </ul>
    176         *
    177         * @param initialArray initial array
    178         * @since 2.2
    179         */
    180        public ResizableDoubleArray(double[] initialArray) {
    181            if (initialArray == null) {
    182                this.internalArray = new double[initialCapacity];
    183            } else {
    184                this.internalArray = new double[initialArray.length];
    185                System.arraycopy(initialArray, 0, this.internalArray, 0, initialArray.length);
    186                initialCapacity = initialArray.length;
    187                numElements = initialArray.length;
    188            }
    189        }
    190
    191        /**
    192         * <p>
    193         * Create a ResizableArray with the specified initial capacity
    194         * and expansion factor.  The remaining properties take default
    195         * values:
    196         * <ul>
    197         * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
    198         * <li><code>contractionFactor = 0.5 + expansionFactor</code></li>
    199         * </ul></p>
    200         * <p>
    201         * Throws IllegalArgumentException if the following conditions are
    202         * not met:
    203         * <ul>
    204         * <li><code>initialCapacity > 0</code></li>
    205         * <li><code>expansionFactor > 1</code></li>
    206         * </ul></p>
    207         *
    208         * @param initialCapacity The initial size of the internal storage array
    209         * @param expansionFactor the array will be expanded based on this
    210         *                        parameter
    211         * @throws IllegalArgumentException if parameters are not valid
    212         */
    213        public ResizableDoubleArray(int initialCapacity, float expansionFactor) {
    214            this.expansionFactor = expansionFactor;
    215            setInitialCapacity(initialCapacity);
    216            internalArray = new double[initialCapacity];
    217            setContractionCriteria(expansionFactor +0.5f);
    218        }
    219
    220        /**
    221         * <p>
    222         * Create a ResizableArray with the specified initialCapacity,
    223         * expansionFactor, and contractionCriteria. The <code>expansionMode</code>
    224         * will default to <code>MULTIPLICATIVE_MODE.</code></p>
    225         * <p>
    226         * Throws IllegalArgumentException if the following conditions are
    227         * not met:
    228         * <ul>
    229         * <li><code>initialCapacity > 0</code></li>
    230         * <li><code>expansionFactor > 1</code></li>
    231         * <li><code>contractionFactor >= expansionFactor</code></li>
    232         * </ul></p>
    233         * @param initialCapacity The initial size of the internal storage array
    234         * @param expansionFactor the array will be expanded based on this
    235         *                        parameter
    236         * @param contractionCriteria The contraction Criteria.
    237         * @throws IllegalArgumentException if parameters are not valid
    238         */
    239        public ResizableDoubleArray(int initialCapacity, float expansionFactor,
    240            float contractionCriteria) {
    241            this.expansionFactor = expansionFactor;
    242            setContractionCriteria(contractionCriteria);
    243            setInitialCapacity(initialCapacity);
    244            internalArray = new double[initialCapacity];
    245        }
    246
    247        /**
    248         * <p>
    249         * Create a ResizableArray with the specified properties.</p>
    250        * <p>
    251         * Throws IllegalArgumentException if the following conditions are
    252         * not met:
    253         * <ul>
    254         * <li><code>initialCapacity > 0</code></li>
    255         * <li><code>expansionFactor > 1</code></li>
    256         * <li><code>contractionFactor >= expansionFactor</code></li>
    257         * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code>
    258         * </li>
    259         * </ul></p>
    260         *
    261         * @param initialCapacity the initial size of the internal storage array
    262         * @param expansionFactor the array will be expanded based on this
    263         *                        parameter
    264         * @param contractionCriteria the contraction Criteria
    265         * @param expansionMode  the expansion mode
    266         * @throws IllegalArgumentException if parameters are not valid
    267         */
    268        public ResizableDoubleArray(int initialCapacity, float expansionFactor,
    269                float contractionCriteria, int expansionMode) {
    270            this.expansionFactor = expansionFactor;
    271            setContractionCriteria(contractionCriteria);
    272            setInitialCapacity(initialCapacity);
    273            setExpansionMode(expansionMode);
    274            internalArray = new double[initialCapacity];
    275        }
    276
    277        /**
    278         * Copy constructor.  Creates a new ResizableDoubleArray that is a deep,
    279         * fresh copy of the original. Needs to acquire synchronization lock
    280         * on original.  Original may not be null; otherwise a NullPointerException
    281         * is thrown.
    282         *
    283         * @param original array to copy
    284         * @since 2.0
    285         */
    286        public ResizableDoubleArray(ResizableDoubleArray original) {
    287            copy(original, this);
    288        }
    289
    290        /**
    291         * Adds an element to the end of this expandable array.
    292         *
    293         * @param value to be added to end of array
    294         */
    295        public synchronized void addElement(double value) {
    296            numElements++;
    297            if ((startIndex + numElements) > internalArray.length) {
    298                expand();
    299            }
    300            internalArray[startIndex + (numElements - 1)] = value;
    301            if (shouldContract()) {
    302                contract();
    303            }
    304        }
    305
    306        /**
    307         * Adds several element to the end of this expandable array.
    308         *
    309         * @param values to be added to end of array
    310         * @since 2.2
    311         */
    312        public synchronized void addElements(double[] values) {
    313            final double[] tempArray = new double[numElements + values.length + 1];
    314            System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
    315            System.arraycopy(values, 0, tempArray, numElements, values.length);
    316            internalArray = tempArray;
    317            startIndex = 0;
    318            numElements += values.length;
    319        }
    320
    321        /**
    322         * <p>
    323         * Adds an element to the end of the array and removes the first
    324         * element in the array.  Returns the discarded first element.
    325         * The effect is similar to a push operation in a FIFO queue.
    326         * </p>
    327         * <p>
    328         * Example: If the array contains the elements 1, 2, 3, 4 (in that order)
    329         * and addElementRolling(5) is invoked, the result is an array containing
    330         * the entries 2, 3, 4, 5 and the value returned is 1.
    331         * </p>
    332         *
    333         * @param value the value to be added to the array
    334         * @return the value which has been discarded or "pushed" out of the array
    335         *         by this rolling insert
    336         */
    337        public synchronized double addElementRolling(double value) {
    338            double discarded = internalArray[startIndex];
    339
    340            if ((startIndex + (numElements + 1)) > internalArray.length) {
    341                expand();
    342            }
    343            // Increment the start index
    344            startIndex += 1;
    345
    346            // Add the new value
    347            internalArray[startIndex + (numElements - 1)] = value;
    348
    349            // Check the contraction criteria
    350            if (shouldContract()) {
    351                contract();
    352            }
    353            return discarded;
    354        }
    355
    356        /**
    357         * Substitutes <code>value</code> for the most recently added value.
    358         * Returns the value that has been replaced. If the array is empty (i.e.
    359         * if {@link #numElements} is zero), a MathRuntimeException is thrown.
    360         *
    361         * @param value new value to substitute for the most recently added value
    362         * @return value that has been replaced in the array
    363         * @since 2.0
    364         */
    365        public synchronized double substituteMostRecentElement(double value) {
    366            if (numElements < 1) {
    367                throw MathRuntimeException.createArrayIndexOutOfBoundsException(
    368                        LocalizedFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY);
    369            }
    370
    371            double discarded = internalArray[startIndex + (numElements - 1)];
    372
    373            internalArray[startIndex + (numElements - 1)] = value;
    374
    375            return discarded;
    376        }
    377
    378
    379        /**
    380         * Checks the expansion factor and the contraction criteria and throws an
    381         * IllegalArgumentException if the contractionCriteria is less than the
    382         * expansionCriteria
    383         *
    384         * @param expansion factor to be checked
    385         * @param contraction criteria to be checked
    386         * @throws IllegalArgumentException if the contractionCriteria is less than
    387         *         the expansionCriteria.
    388         */
    389        protected void checkContractExpand(float contraction, float expansion) {
    390
    391            if (contraction < expansion) {
    392                throw MathRuntimeException.createIllegalArgumentException(
    393                        LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR,
    394                        contraction, expansion);
    395            }
    396
    397            if (contraction <= 1.0) {
    398                throw MathRuntimeException.createIllegalArgumentException(
    399                        LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE,
    400                        contraction);
    401            }
    402
    403            if (expansion <= 1.0) {
    404                throw MathRuntimeException.createIllegalArgumentException(
    405                        LocalizedFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE,
    406                        expansion);
    407            }
    408        }
    409
    410        /**
    411         * Clear the array, reset the size to the initialCapacity and the number
    412         * of elements to zero.
    413         */
    414        public synchronized void clear() {
    415            numElements = 0;
    416            startIndex = 0;
    417            internalArray = new double[initialCapacity];
    418        }
    419
    420        /**
    421         * Contracts the storage array to the (size of the element set) + 1 - to
    422         * avoid a zero length array. This function also resets the startIndex to
    423         * zero.
    424         */
    425        public synchronized void contract() {
    426            double[] tempArray = new double[numElements + 1];
    427
    428            // Copy and swap - copy only the element array from the src array.
    429            System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
    430            internalArray = tempArray;
    431
    432            // Reset the start index to zero
    433            startIndex = 0;
    434        }
    435
    436        /**
    437         * Discards the <code>i<code> initial elements of the array.  For example,
    438         * if the array contains the elements 1,2,3,4, invoking
    439         * <code>discardFrontElements(2)</code> will cause the first two elements
    440         * to be discarded, leaving 3,4 in the array.  Throws illegalArgumentException
    441         * if i exceeds numElements.
    442         *
    443         * @param i  the number of elements to discard from the front of the array
    444         * @throws IllegalArgumentException if i is greater than numElements.
    445         * @since 2.0
    446         */
    447        public synchronized void discardFrontElements(int i) {
    448
    449            discardExtremeElements(i,true);
    450
    451        }
    452
    453        /**
    454         * Discards the <code>i<code> last elements of the array.  For example,
    455         * if the array contains the elements 1,2,3,4, invoking
    456         * <code>discardMostRecentElements(2)</code> will cause the last two elements
    457         * to be discarded, leaving 1,2 in the array.  Throws illegalArgumentException
    458         * if i exceeds numElements.
    459         *
    460         * @param i  the number of elements to discard from the end of the array
    461         * @throws IllegalArgumentException if i is greater than numElements.
    462         * @since 2.0
    463         */
    464        public synchronized void discardMostRecentElements(int i) {
    465
    466            discardExtremeElements(i,false);
    467
    468        }
    469
    470        /**
    471         * Discards the <code>i<code> first or last elements of the array,
    472         * depending on the value of <code>front</code>.
    473         * For example, if the array contains the elements 1,2,3,4, invoking
    474         * <code>discardExtremeElements(2,false)</code> will cause the last two elements
    475         * to be discarded, leaving 1,2 in the array.
    476         * For example, if the array contains the elements 1,2,3,4, invoking
    477         * <code>discardExtremeElements(2,true)</code> will cause the first two elements
    478         * to be discarded, leaving 3,4 in the array.
    479         * Throws illegalArgumentException
    480         * if i exceeds numElements.
    481         *
    482         * @param i  the number of elements to discard from the front/end of the array
    483         * @param front true if elements are to be discarded from the front
    484         * of the array, false if elements are to be discarded from the end
    485         * of the array
    486         * @throws IllegalArgumentException if i is greater than numElements.
    487         * @since 2.0
    488         */
    489        private synchronized void discardExtremeElements(int i,boolean front) {
    490            if (i > numElements) {
    491                throw MathRuntimeException.createIllegalArgumentException(
    492                        LocalizedFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY,
    493                        i, numElements);
    494           } else if (i < 0) {
    495               throw MathRuntimeException.createIllegalArgumentException(
    496                       LocalizedFormats.CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS,
    497                       i);
    498            } else {
    499                // "Subtract" this number of discarded from numElements
    500                numElements -= i;
    501                if (front) startIndex += i;
    502            }
    503            if (shouldContract()) {
    504                contract();
    505            }
    506        }
    507
    508        /**
    509         * Expands the internal storage array using the expansion factor.
    510         * <p>
    511         * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE,
    512         * the new array size will be <code>internalArray.length * expansionFactor.</code>
    513         * If <code>expansionMode</code> is set to ADDITIVE_MODE,  the length
    514         * after expansion will be <code>internalArray.length + expansionFactor</code>
    515         * </p>
    516         */
    517        protected synchronized void expand() {
    518
    519            // notice the use of FastMath.ceil(), this guarantees that we will always
    520            // have an array of at least currentSize + 1.   Assume that the
    521            // current initial capacity is 1 and the expansion factor
    522            // is 1.000000000000000001.  The newly calculated size will be
    523            // rounded up to 2 after the multiplication is performed.
    524            int newSize = 0;
    525            if (expansionMode == MULTIPLICATIVE_MODE) {
    526                newSize = (int) FastMath.ceil(internalArray.length * expansionFactor);
    527            } else {
    528                newSize = internalArray.length + FastMath.round(expansionFactor);
    529            }
    530            double[] tempArray = new double[newSize];
    531
    532            // Copy and swap
    533            System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
    534            internalArray = tempArray;
    535        }
    536
    537        /**
    538         * Expands the internal storage array to the specified size.
    539         *
    540         * @param size Size of the new internal storage array
    541         */
    542        private synchronized void expandTo(int size) {
    543            double[] tempArray = new double[size];
    544            // Copy and swap
    545            System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
    546            internalArray = tempArray;
    547        }
    548
    549        /**
    550         * The contraction criteria defines when the internal array will contract
    551         * to store only the number of elements in the element array.
    552         * If  the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>,
    553         * contraction is triggered when the ratio between storage array length
    554         * and <code>numElements</code> exceeds <code>contractionFactor</code>.
    555         * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the
    556         * number of excess storage locations is compared to
    557         * <code>contractionFactor.</code>
    558         *
    559         * @return the contraction criteria used to reclaim memory.
    560         */
    561        public float getContractionCriteria() {
    562            return contractionCriteria;
    563        }
    564
    565        /**
    566         * Returns the element at the specified index
    567         *
    568         * @param index index to fetch a value from
    569         * @return value stored at the specified index
    570         * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
    571         *         zero or is greater than <code>getNumElements() - 1</code>.
    572         */
    573        public synchronized double getElement(int index) {
    574            if (index >= numElements) {
    575                throw MathRuntimeException.createArrayIndexOutOfBoundsException(
    576                        LocalizedFormats.INDEX_LARGER_THAN_MAX,
    577                        index, numElements - 1);
    578            } else if (index >= 0) {
    579                return internalArray[startIndex + index];
    580            } else {
    581                throw MathRuntimeException.createArrayIndexOutOfBoundsException(
    582                        LocalizedFormats.CANNOT_RETRIEVE_AT_NEGATIVE_INDEX,
    583                        index);
    584            }
    585        }
    586
    587         /**
    588         * Returns a double array containing the elements of this
    589         * <code>ResizableArray</code>.  This method returns a copy, not a
    590         * reference to the underlying array, so that changes made to the returned
    591         *  array have no effect on this <code>ResizableArray.</code>
    592         * @return the double array.
    593         */
    594        public synchronized double[] getElements() {
    595            double[] elementArray = new double[numElements];
    596            System.arraycopy( internalArray, startIndex, elementArray, 0,
    597                    numElements);
    598            return elementArray;
    599        }
    600
    601        /**
    602         * The expansion factor controls the size of a new array when an array
    603         * needs to be expanded.  The <code>expansionMode</code>
    604         * determines whether the size of the array is multiplied by the
    605         * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
    606         * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
    607         * storage locations added).  The default <code>expansionMode</code> is
    608         * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
    609         * is 2.0.
    610         *
    611         * @return the expansion factor of this expandable double array
    612         */
    613        public float getExpansionFactor() {
    614            return expansionFactor;
    615        }
    616
    617        /**
    618         * The <code>expansionMode</code> determines whether the internal storage
    619         * array grows additively (ADDITIVE_MODE) or multiplicatively
    620         * (MULTIPLICATIVE_MODE) when it is expanded.
    621         *
    622         * @return Returns the expansionMode.
    623         */
    624        public int getExpansionMode() {
    625            return expansionMode;
    626        }
    627
    628        /**
    629         * Notice the package scope on this method.   This method is simply here
    630         * for the JUnit test, it allows us check if the expansion is working
    631         * properly after a number of expansions.  This is not meant to be a part
    632         * of the public interface of this class.
    633         *
    634         * @return the length of the internal storage array.
    635         */
    636        synchronized int getInternalLength() {
    637            return internalArray.length;
    638        }
    639
    640        /**
    641         * Returns the number of elements currently in the array.  Please note
    642         * that this is different from the length of the internal storage array.
    643         *
    644         * @return number of elements
    645         */
    646        public synchronized int getNumElements() {
    647            return numElements;
    648        }
    649
    650        /**
    651         * Returns the internal storage array.  Note that this method returns
    652         * a reference to the internal storage array, not a copy, and to correctly
    653         * address elements of the array, the <code>startIndex</code> is
    654         * required (available via the {@link #start} method).  This method should
    655         * only be used in cases where copying the internal array is not practical.
    656         * The {@link #getElements} method should be used in all other cases.
    657         *
    658         *
    659         * @return the internal storage array used by this object
    660         * @deprecated replaced by {@link #getInternalValues()} as of 2.0
    661         */
    662        @Deprecated
    663        public synchronized double[] getValues() {
    664            return internalArray;
    665        }
    666
    667        /**
    668         * Returns the internal storage array.  Note that this method returns
    669         * a reference to the internal storage array, not a copy, and to correctly
    670         * address elements of the array, the <code>startIndex</code> is
    671         * required (available via the {@link #start} method).  This method should
    672         * only be used in cases where copying the internal array is not practical.
    673         * The {@link #getElements} method should be used in all other cases.
    674         *
    675         *
    676         * @return the internal storage array used by this object
    677         * @since 2.0
    678         */
    679        public synchronized double[] getInternalValues() {
    680            return internalArray;
    681        }
    682
    683        /**
    684         * Sets the contraction criteria for this ExpandContractDoubleArray.
    685         *
    686         * @param contractionCriteria contraction criteria
    687         */
    688        public void setContractionCriteria(float contractionCriteria) {
    689            checkContractExpand(contractionCriteria, getExpansionFactor());
    690            synchronized(this) {
    691                this.contractionCriteria = contractionCriteria;
    692            }
    693        }
    694
    695
    696        /**
    697         * Sets the element at the specified index.  If the specified index is greater than
    698         * <code>getNumElements() - 1</code>, the <code>numElements</code> property
    699         * is increased to <code>index +1</code> and additional storage is allocated
    700         * (if necessary) for the new element and all  (uninitialized) elements
    701         * between the new element and the previous end of the array).
    702         *
    703         * @param index index to store a value in
    704         * @param value value to store at the specified index
    705         * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
    706         *         zero.
    707         */
    708        public synchronized void setElement(int index, double value) {
    709            if (index < 0) {
    710                throw MathRuntimeException.createArrayIndexOutOfBoundsException(
    711                        LocalizedFormats.CANNOT_SET_AT_NEGATIVE_INDEX,
    712                        index);
    713            }
    714            if (index + 1 > numElements) {
    715                numElements = index + 1;
    716            }
    717            if ((startIndex + index) >= internalArray.length) {
    718                expandTo(startIndex + (index + 1));
    719            }
    720            internalArray[startIndex + index] = value;
    721        }
    722
    723        /**
    724         * Sets the expansionFactor.  Throws IllegalArgumentException if the
    725         * the following conditions are not met:
    726         * <ul>
    727         * <li><code>expansionFactor > 1</code></li>
    728         * <li><code>contractionFactor >= expansionFactor</code></li>
    729         * </ul>
    730         * @param expansionFactor the new expansion factor value.
    731         * @throws IllegalArgumentException if expansionFactor is <= 1 or greater
    732         * than contractionFactor
    733         */
    734        public void setExpansionFactor(float expansionFactor) {
    735            checkContractExpand(getContractionCriteria(), expansionFactor);
    736            // The check above verifies that the expansion factor is > 1.0;
    737            synchronized(this) {
    738                this.expansionFactor = expansionFactor;
    739            }
    740        }
    741
    742        /**
    743         * Sets the <code>expansionMode</code>. The specified value must be one of
    744         * ADDITIVE_MODE, MULTIPLICATIVE_MODE.
    745         *
    746         * @param expansionMode The expansionMode to set.
    747         * @throws IllegalArgumentException if the specified mode value is not valid
    748         */
    749        public void setExpansionMode(int expansionMode) {
    750            if (expansionMode != MULTIPLICATIVE_MODE &&
    751                    expansionMode != ADDITIVE_MODE) {
    752                throw MathRuntimeException.createIllegalArgumentException(
    753                        LocalizedFormats.UNSUPPORTED_EXPANSION_MODE,
    754                        expansionMode, MULTIPLICATIVE_MODE, "MULTIPLICATIVE_MODE",
    755                        ADDITIVE_MODE, "ADDITIVE_MODE");
    756            }
    757            synchronized(this) {
    758                this.expansionMode = expansionMode;
    759            }
    760        }
    761
    762        /**
    763         * Sets the initial capacity.  Should only be invoked by constructors.
    764         *
    765         * @param initialCapacity of the array
    766         * @throws IllegalArgumentException if <code>initialCapacity</code> is not
    767         *         positive.
    768         */
    769        protected void setInitialCapacity(int initialCapacity) {
    770            if (initialCapacity > 0) {
    771                synchronized(this) {
    772                    this.initialCapacity = initialCapacity;
    773                }
    774            } else {
    775                throw MathRuntimeException.createIllegalArgumentException(
    776                        LocalizedFormats.INITIAL_CAPACITY_NOT_POSITIVE,
    777                        initialCapacity);
    778            }
    779        }
    780
    781        /**
    782         * This function allows you to control the number of elements contained
    783         * in this array, and can be used to "throw out" the last n values in an
    784         * array. This function will also expand the internal array as needed.
    785         *
    786         * @param i a new number of elements
    787         * @throws IllegalArgumentException if <code>i</code> is negative.
    788         */
    789        public synchronized void setNumElements(int i) {
    790
    791            // If index is negative thrown an error
    792            if (i < 0) {
    793                throw MathRuntimeException.createIllegalArgumentException(
    794                        LocalizedFormats.INDEX_NOT_POSITIVE,
    795                        i);
    796            }
    797
    798            // Test the new num elements, check to see if the array needs to be
    799            // expanded to accommodate this new number of elements
    800            if ((startIndex + i) > internalArray.length) {
    801                expandTo(startIndex + i);
    802            }
    803
    804            // Set the new number of elements to new value
    805            numElements = i;
    806        }
    807
    808        /**
    809         * Returns true if the internal storage array has too many unused
    810         * storage positions.
    811         *
    812         * @return true if array satisfies the contraction criteria
    813         */
    814        private synchronized boolean shouldContract() {
    815            if (expansionMode == MULTIPLICATIVE_MODE) {
    816                return (internalArray.length / ((float) numElements)) > contractionCriteria;
    817            } else {
    818                return (internalArray.length - numElements) > contractionCriteria;
    819            }
    820        }
    821
    822        /**
    823         * Returns the starting index of the internal array.  The starting index is
    824         * the position of the first addressable element in the internal storage
    825         * array.  The addressable elements in the array are <code>
    826         * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
    827         * </code>
    828         *
    829         * @return starting index
    830         */
    831        public synchronized int start() {
    832            return startIndex;
    833        }
    834
    835        /**
    836         * <p>Copies source to dest, copying the underlying data, so dest is
    837         * a new, independent copy of source.  Does not contract before
    838         * the copy.</p>
    839         *
    840         * <p>Obtains synchronization locks on both source and dest
    841         * (in that order) before performing the copy.</p>
    842         *
    843         * <p>Neither source nor dest may be null; otherwise a NullPointerException
    844         * is thrown</p>
    845         *
    846         * @param source ResizableDoubleArray to copy
    847         * @param dest ResizableArray to replace with a copy of the source array
    848         * @since 2.0
    849         *
    850         */
    851        public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest) {
    852           synchronized(source) {
    853               synchronized(dest) {
    854                   dest.initialCapacity = source.initialCapacity;
    855                   dest.contractionCriteria = source.contractionCriteria;
    856                   dest.expansionFactor = source.expansionFactor;
    857                   dest.expansionMode = source.expansionMode;
    858                   dest.internalArray = new double[source.internalArray.length];
    859                   System.arraycopy(source.internalArray, 0, dest.internalArray,
    860                           0, dest.internalArray.length);
    861                   dest.numElements = source.numElements;
    862                   dest.startIndex = source.startIndex;
    863               }
    864           }
    865        }
    866
    867        /**
    868         * Returns a copy of the ResizableDoubleArray.  Does not contract before
    869         * the copy, so the returned object is an exact copy of this.
    870         *
    871         * @return a new ResizableDoubleArray with the same data and configuration
    872         * properties as this
    873         * @since 2.0
    874         */
    875        public synchronized ResizableDoubleArray copy() {
    876            ResizableDoubleArray result = new ResizableDoubleArray();
    877            copy(this, result);
    878            return result;
    879        }
    880
    881        /**
    882         * Returns true iff object is a ResizableDoubleArray with the same properties
    883         * as this and an identical internal storage array.
    884         *
    885         * @param object object to be compared for equality with this
    886         * @return true iff object is a ResizableDoubleArray with the same data and
    887         * properties as this
    888         * @since 2.0
    889         */
    890        @Override
    891        public boolean equals(Object object) {
    892            if (object == this ) {
    893                return true;
    894            }
    895           if (object instanceof ResizableDoubleArray == false) {
    896                return false;
    897            }
    898           synchronized(this) {
    899               synchronized(object) {
    900                   boolean result = true;
    901                   ResizableDoubleArray other = (ResizableDoubleArray) object;
    902                   result = result && (other.initialCapacity == initialCapacity);
    903                   result = result && (other.contractionCriteria == contractionCriteria);
    904                   result = result && (other.expansionFactor == expansionFactor);
    905                   result = result && (other.expansionMode == expansionMode);
    906                   result = result && (other.numElements == numElements);
    907                   result = result && (other.startIndex == startIndex);
    908                   if (!result) {
    909                       return false;
    910                   } else {
    911                       return Arrays.equals(internalArray, other.internalArray);
    912                   }
    913               }
    914           }
    915        }
    916
    917        /**
    918         * Returns a hash code consistent with equals.
    919         *
    920         * @return hash code representing this ResizableDoubleArray
    921         * @since 2.0
    922         */
    923        @Override
    924        public synchronized int hashCode() {
    925            int[] hashData = new int[7];
    926            hashData[0] = new Float(expansionFactor).hashCode();
    927            hashData[1] = new Float(contractionCriteria).hashCode();
    928            hashData[2] = expansionMode;
    929                hashData[3] = Arrays.hashCode(internalArray);
    930                hashData[4] = initialCapacity;
    931                hashData[5] = numElements;
    932                hashData[6] = startIndex;
    933            return Arrays.hashCode(hashData);
    934        }
    935
    936    }