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    }