Home | History | Annotate | Download | only in util
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 package org.apache.commons.math.util;
     18 
     19 import java.io.Serializable;
     20 import java.util.Arrays;
     21 
     22 import org.apache.commons.math.MathRuntimeException;
     23 import org.apache.commons.math.exception.util.LocalizedFormats;
     24 
     25 /**
     26  * <p>
     27  * A variable length {@link DoubleArray} implementation that automatically
     28  * handles expanding and contracting its internal storage array as elements
     29  * are added and removed.
     30  * </p>
     31  * <p>
     32  *  The internal storage array starts with capacity determined by the
     33  * <code>initialCapacity</code> property, which can be set by the constructor.
     34  * The default initial capacity is 16.  Adding elements using
     35  * {@link #addElement(double)} appends elements to the end of the array.  When
     36  * there are no open entries at the end of the internal storage array, the
     37  * array is expanded.  The size of the expanded array depends on the
     38  * <code>expansionMode</code> and <code>expansionFactor</code> properties.
     39  * The <code>expansionMode</code> determines whether the size of the array is
     40  * multiplied by the <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
     41  * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
     42  * storage locations added).  The default <code>expansionMode</code> is
     43  * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
     44  * is 2.0.
     45  * </p>
     46  * <p>
     47  * The {@link #addElementRolling(double)} method adds a new element to the end
     48  * of the internal storage array and adjusts the "usable window" of the
     49  * internal array forward by one position (effectively making what was the
     50  * second element the first, and so on).  Repeated activations of this method
     51  * (or activation of {@link #discardFrontElements(int)}) will effectively orphan
     52  * the storage locations at the beginning of the internal storage array.  To
     53  * reclaim this storage, each time one of these methods is activated, the size
     54  * of the internal storage array is compared to the number of addressable
     55  * elements (the <code>numElements</code> property) and if the difference
     56  * is too large, the internal array is contracted to size
     57  * <code>numElements + 1.</code>  The determination of when the internal
     58  * storage array is "too large" depends on the <code>expansionMode</code> and
     59  * <code>contractionFactor</code> properties.  If  the <code>expansionMode</code>
     60  * is <code>MULTIPLICATIVE_MODE</code>, contraction is triggered when the
     61  * ratio between storage array length and <code>numElements</code> exceeds
     62  * <code>contractionFactor.</code>  If the <code>expansionMode</code>
     63  * is <code>ADDITIVE_MODE,</code> the number of excess storage locations
     64  * is compared to <code>contractionFactor.</code>
     65  * </p>
     66  * <p>
     67  * To avoid cycles of expansions and contractions, the
     68  * <code>expansionFactor</code> must not exceed the
     69  * <code>contractionFactor.</code> Constructors and mutators for both of these
     70  * properties enforce this requirement, throwing IllegalArgumentException if it
     71  * is violated.
     72  * </p>
     73  * @version $Revision: 1073474 $ $Date: 2011-02-22 20:52:17 +0100 (mar. 22 fvr. 2011) $
     74  */
     75 public class ResizableDoubleArray implements DoubleArray, Serializable {
     76 
     77     /** additive expansion mode */
     78     public static final int ADDITIVE_MODE = 1;
     79 
     80     /** multiplicative expansion mode */
     81     public static final int MULTIPLICATIVE_MODE = 0;
     82 
     83     /** Serializable version identifier */
     84     private static final long serialVersionUID = -3485529955529426875L;
     85 
     86     /**
     87      * The contraction criteria determines when the internal array will be
     88      * contracted to fit the number of elements contained in the element
     89      *  array + 1.
     90      */
     91     protected float contractionCriteria = 2.5f;
     92 
     93     /**
     94      * The expansion factor of the array.  When the array needs to be expanded,
     95      * the new array size will be
     96      * <code>internalArray.length * expansionFactor</code>
     97      * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, or
     98      * <code>internalArray.length + expansionFactor</code> if
     99      * <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 }
    937