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