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: 796021 $ $Date: 2009-07-20 17:27:12 -0400 (Mon, 20 Jul 2009) $
073     */
074    public class ResizableDoubleArray implements DoubleArray, Serializable {
075        
076        /** Serializable version identifier */
077        private static final long serialVersionUID = -3485529955529426875L; 
078        
079        /** additive expansion mode */
080        public static final int ADDITIVE_MODE = 1;
081        
082        /** multiplicative expansion mode */
083        public static final int MULTIPLICATIVE_MODE = 0;
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 expansionFactor factor to be checked
341         * @param contractionCriteria criteria to be checked
342         * @throws IllegalArgumentException if the contractionCriteria is less than
343         *         the expansionCriteria.
344         */
345        protected void checkContractExpand(
346            float contractionCriteria,
347            float expansionFactor) {
348    
349            if (contractionCriteria < expansionFactor) {
350                throw MathRuntimeException.createIllegalArgumentException(
351                        "contraction criteria ({0}) smaller than the expansion factor ({1}).  This would " +
352                        "lead to a never ending loop of expansion and contraction as a newly expanded " +
353                        "internal storage array would immediately satisfy the criteria for contraction",
354                        contractionCriteria, expansionFactor);
355            }
356    
357            if (contractionCriteria <= 1.0) {
358                throw MathRuntimeException.createIllegalArgumentException(
359                        "contraction criteria smaller than one ({0}).  This would lead to a never ending " +
360                        "loop of expansion and contraction as an internal storage array length equal " +
361                        "to the number of elements would satisfy the contraction criteria.",
362                        contractionCriteria);
363            }
364    
365            if (expansionFactor <= 1.0) {
366                throw MathRuntimeException.createIllegalArgumentException(
367                        "expansion factor smaller than one ({0})",
368                        expansionFactor);
369            }
370        }
371        
372        /**
373         * Clear the array, reset the size to the initialCapacity and the number 
374         * of elements to zero.
375         */
376        public synchronized void clear() {
377            numElements = 0;
378            startIndex = 0;
379            internalArray = new double[initialCapacity];
380        }
381        
382        /**
383         * Contracts the storage array to the (size of the element set) + 1 - to 
384         * avoid a zero length array. This function also resets the startIndex to 
385         * zero. 
386         */
387        public synchronized void contract() {
388            double[] tempArray = new double[numElements + 1];
389    
390            // Copy and swap - copy only the element array from the src array.
391            System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
392            internalArray = tempArray;
393    
394            // Reset the start index to zero
395            startIndex = 0;
396        }
397    
398        /**
399         * Discards the <code>i<code> initial elements of the array.  For example,
400         * if the array contains the elements 1,2,3,4, invoking 
401         * <code>discardFrontElements(2)</code> will cause the first two elements 
402         * to be discarded, leaving 3,4 in the array.  Throws illegalArgumentException
403         * if i exceeds numElements.
404         * 
405         * @param i  the number of elements to discard from the front of the array
406         * @throws IllegalArgumentException if i is greater than numElements.
407         * @since 2.0
408         */
409        public synchronized void discardFrontElements(int i) {
410    
411            discardExtremeElements(i,true);
412            
413        }
414    
415        /**
416         * Discards the <code>i<code> last elements of the array.  For example,
417         * if the array contains the elements 1,2,3,4, invoking 
418         * <code>discardMostRecentElements(2)</code> will cause the last two elements 
419         * to be discarded, leaving 1,2 in the array.  Throws illegalArgumentException
420         * if i exceeds numElements.
421         * 
422         * @param i  the number of elements to discard from the end of the array
423         * @throws IllegalArgumentException if i is greater than numElements.
424         * @since 2.0
425         */
426        public synchronized void discardMostRecentElements(int i) {
427    
428            discardExtremeElements(i,false);
429            
430        }
431        
432        /**
433         * Discards the <code>i<code> first or last elements of the array,
434         * depending on the value of <code>front</code>.
435         * For example, if the array contains the elements 1,2,3,4, invoking 
436         * <code>discardExtremeElements(2,false)</code> will cause the last two elements 
437         * to be discarded, leaving 1,2 in the array.
438         * For example, if the array contains the elements 1,2,3,4, invoking 
439         * <code>discardExtremeElements(2,true)</code> will cause the first two elements 
440         * to be discarded, leaving 3,4 in the array.
441         * Throws illegalArgumentException
442         * if i exceeds numElements.
443         * 
444         * @param i  the number of elements to discard from the front/end of the array
445         * @param front true if elements are to be discarded from the front
446         * of the array, false if elements are to be discarded from the end
447         * of the array 
448         * @throws IllegalArgumentException if i is greater than numElements.
449         * @since 2.0
450         */
451        private synchronized void discardExtremeElements(int i,boolean front) {
452            if (i > numElements) {
453                throw MathRuntimeException.createIllegalArgumentException(
454                        "cannot discard {0} elements from a {1} elements array",
455                        i, numElements);
456           } else if (i < 0) {
457               throw MathRuntimeException.createIllegalArgumentException(
458                       "cannot discard a negative number of elements ({0})",
459                       i);
460            } else {
461                // "Subtract" this number of discarded from numElements 
462                numElements -= i;
463                if (front) startIndex += i;
464            }
465            if (shouldContract()) {
466                contract();
467            }
468        }
469    
470        /**
471         * Expands the internal storage array using the expansion factor.
472         * <p>
473         * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE,
474         * the new array size will be <code>internalArray.length * expansionFactor.</code>
475         * If <code>expansionMode</code> is set to ADDITIVE_MODE,  the length
476         * after expansion will be <code>internalArray.length + expansionFactor</code>
477         * </p>
478         */
479        protected synchronized void expand() {
480    
481            // notice the use of Math.ceil(), this guarantees that we will always 
482            // have an array of at least currentSize + 1.   Assume that the 
483            // current initial capacity is 1 and the expansion factor
484            // is 1.000000000000000001.  The newly calculated size will be 
485            // rounded up to 2 after the multiplication is performed.
486            int newSize = 0;
487            if (expansionMode == MULTIPLICATIVE_MODE) {
488                newSize = (int) Math.ceil(internalArray.length * expansionFactor);
489            } else {
490                newSize = internalArray.length + Math.round(expansionFactor);
491            }
492            double[] tempArray = new double[newSize];
493    
494            // Copy and swap
495            System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
496            internalArray = tempArray;
497        }
498        
499        /**
500         * Expands the internal storage array to the specified size.
501         * 
502         * @param size Size of the new internal storage array
503         */
504        private synchronized void expandTo(int size) {
505            double[] tempArray = new double[size];
506            // Copy and swap
507            System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
508            internalArray = tempArray;
509        }
510    
511        /**
512         * The contraction criteria defines when the internal array will contract 
513         * to store only the number of elements in the element array.   
514         * If  the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>,
515         * contraction is triggered when the ratio between storage array length 
516         * and <code>numElements</code> exceeds <code>contractionFactor</code>.
517         * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the
518         * number of excess storage locations is compared to 
519         * <code>contractionFactor.</code>   
520         * 
521         * @return the contraction criteria used to reclaim memory.
522         */
523        public float getContractionCriteria() {
524            return contractionCriteria;
525        }
526        
527        /**
528         * Returns the element at the specified index
529         * 
530         * @param index index to fetch a value from
531         * @return value stored at the specified index
532         * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
533         *         zero or is greater than <code>getNumElements() - 1</code>.
534         */
535        public synchronized double getElement(int index) {
536            if (index >= numElements) {
537                throw MathRuntimeException.createArrayIndexOutOfBoundsException(
538                        "the index specified: {0} is larger than the current maximal index {1}",
539                        index, numElements - 1);
540            } else if (index >= 0) {
541                return internalArray[startIndex + index];
542            } else {
543                throw MathRuntimeException.createArrayIndexOutOfBoundsException(
544                        "elements cannot be retrieved from a negative array index {0}",
545                        index);
546            }
547        }
548        
549         /**
550         * Returns a double array containing the elements of this 
551         * <code>ResizableArray</code>.  This method returns a copy, not a
552         * reference to the underlying array, so that changes made to the returned
553         *  array have no effect on this <code>ResizableArray.</code>
554         * @return the double array.
555         */
556        public synchronized double[] getElements() {
557            double[] elementArray = new double[numElements];
558            System.arraycopy( internalArray, startIndex, elementArray, 0,
559                    numElements);
560            return elementArray;
561        }
562        
563        /**
564         * The expansion factor controls the size of a new array when an array 
565         * needs to be expanded.  The <code>expansionMode</code>
566         * determines whether the size of the array is multiplied by the 
567         * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if 
568         * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
569         * storage locations added).  The default <code>expansionMode</code> is 
570         * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
571         * is 2.0.
572         * 
573         * @return the expansion factor of this expandable double array
574         */
575        public float getExpansionFactor() {
576            return expansionFactor;
577        }
578        
579        /**
580         * The <code>expansionMode</code> determines whether the internal storage 
581         * array grows additively (ADDITIVE_MODE) or multiplicatively 
582         * (MULTIPLICATIVE_MODE) when it is expanded.
583         * 
584         * @return Returns the expansionMode.
585         */
586        public int getExpansionMode() {
587            return expansionMode;
588        }
589        
590        /**
591         * Notice the package scope on this method.   This method is simply here 
592         * for the JUnit test, it allows us check if the expansion is working 
593         * properly after a number of expansions.  This is not meant to be a part 
594         * of the public interface of this class.
595         * 
596         * @return the length of the internal storage array.
597         */
598        synchronized int getInternalLength() {
599            return (internalArray.length);
600        }
601    
602        /**
603         * Returns the number of elements currently in the array.  Please note
604         * that this is different from the length of the internal storage array.  
605         *
606         * @return number of elements
607         */
608        public synchronized int getNumElements() {
609            return (numElements);
610        }
611        
612        /**
613         * Returns the internal storage array.  Note that this method returns
614         * a reference to the internal storage array, not a copy, and to correctly
615         * address elements of the array, the <code>startIndex</code> is
616         * required (available via the {@link #start} method).  This method should
617         * only be used in cases where copying the internal array is not practical.
618         * The {@link #getElements} method should be used in all other cases.
619         *
620         * 
621         * @return the internal storage array used by this object
622         * @deprecated replaced by {@link #getInternalValues()} as of 2.0
623         */
624        @Deprecated
625        public synchronized double[] getValues() {
626            return (internalArray);
627        }
628    
629        /**
630         * Returns the internal storage array.  Note that this method returns
631         * a reference to the internal storage array, not a copy, and to correctly
632         * address elements of the array, the <code>startIndex</code> is
633         * required (available via the {@link #start} method).  This method should
634         * only be used in cases where copying the internal array is not practical.
635         * The {@link #getElements} method should be used in all other cases.
636         *
637         * 
638         * @return the internal storage array used by this object
639         * @since 2.0
640         */
641        public synchronized double[] getInternalValues() {
642            return (internalArray);
643        }
644    
645        /**
646         * Sets the contraction criteria for this ExpandContractDoubleArray. 
647         * 
648         * @param contractionCriteria contraction criteria
649         */
650        public void setContractionCriteria(float contractionCriteria) {
651            checkContractExpand(contractionCriteria, getExpansionFactor());
652            synchronized(this) {
653                this.contractionCriteria = contractionCriteria;
654            }
655        }
656        
657    
658        /**
659         * Sets the element at the specified index.  If the specified index is greater than
660         * <code>getNumElements() - 1</code>, the <code>numElements</code> property
661         * is increased to <code>index +1</code> and additional storage is allocated 
662         * (if necessary) for the new element and all  (uninitialized) elements 
663         * between the new element and the previous end of the array).
664         * 
665         * @param index index to store a value in
666         * @param value value to store at the specified index
667         * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
668         *         zero.
669         */
670        public synchronized void setElement(int index, double value) {
671            if (index < 0) {
672                throw MathRuntimeException.createArrayIndexOutOfBoundsException(
673                        "cannot set an element at a negative index {0}",
674                        index);
675            }
676            if (index + 1 > numElements) {
677                numElements = index + 1;
678            }       
679            if ((startIndex + index) >= internalArray.length) {
680                expandTo(startIndex + (index + 1));
681            }    
682            internalArray[startIndex + index] = value;
683        }
684    
685        /**
686         * Sets the expansionFactor.  Throws IllegalArgumentException if the 
687         * the following conditions are not met:
688         * <ul>
689         * <li><code>expansionFactor > 1</code></li>
690         * <li><code>contractionFactor >= expansionFactor</code></li>
691         * </ul>
692         * @param expansionFactor the new expansion factor value.
693         * @throws IllegalArgumentException if expansionFactor is <= 1 or greater
694         * than contractionFactor
695         */
696        public void setExpansionFactor(float expansionFactor) {
697            checkContractExpand(getContractionCriteria(), expansionFactor);
698            // The check above verifies that the expansion factor is > 1.0;
699            synchronized(this) {
700                this.expansionFactor = expansionFactor;
701            }
702        }
703    
704        /**
705         * Sets the <code>expansionMode</code>. The specified value must be one of
706         * ADDITIVE_MODE, MULTIPLICATIVE_MODE.
707         * 
708         * @param expansionMode The expansionMode to set.
709         * @throws IllegalArgumentException if the specified mode value is not valid
710         */
711        public void setExpansionMode(int expansionMode) {
712            if (expansionMode != MULTIPLICATIVE_MODE && 
713                    expansionMode != ADDITIVE_MODE) {
714                throw MathRuntimeException.createIllegalArgumentException(
715                        "unsupported expansion mode {0}, supported modes are {1} ({2}) and {3} ({4})",
716                        expansionMode, MULTIPLICATIVE_MODE, "MULTIPLICATIVE_MODE",
717                        ADDITIVE_MODE, "ADDITIVE_MODE");
718            }
719            synchronized(this) {
720                this.expansionMode = expansionMode;
721            }
722        }
723        
724        /**
725         * Sets the initial capacity.  Should only be invoked by constructors.
726         * 
727         * @param initialCapacity of the array
728         * @throws IllegalArgumentException if <code>initialCapacity</code> is not
729         *         positive.
730         */
731        protected void setInitialCapacity(int initialCapacity) {
732            if (initialCapacity > 0) {
733                synchronized(this) {
734                    this.initialCapacity = initialCapacity;
735                }
736            } else {
737                throw MathRuntimeException.createIllegalArgumentException(
738                        "initial capacity ({0}) is not positive",
739                        initialCapacity);
740            }
741        }
742        
743        /**
744         * This function allows you to control the number of elements contained 
745         * in this array, and can be used to "throw out" the last n values in an 
746         * array. This function will also expand the internal array as needed.
747         * 
748         * @param i a new number of elements
749         * @throws IllegalArgumentException if <code>i</code> is negative.
750         */
751        public synchronized void setNumElements(int i) {
752    
753            // If index is negative thrown an error
754            if (i < 0) {
755                throw MathRuntimeException.createIllegalArgumentException(
756                        "index ({0}) is not positive",
757                        i);
758            }
759    
760            // Test the new num elements, check to see if the array needs to be 
761            // expanded to accommodate this new number of elements
762            if ((startIndex + i) > internalArray.length) {
763                expandTo(startIndex + i);
764            }
765    
766            // Set the new number of elements to new value
767            numElements = i;
768        }
769    
770        /**
771         * Returns true if the internal storage array has too many unused 
772         * storage positions.  
773         * 
774         * @return true if array satisfies the contraction criteria
775         */
776        private synchronized boolean shouldContract() {
777            if (expansionMode == MULTIPLICATIVE_MODE) { 
778                return (internalArray.length / ((float) numElements)) > contractionCriteria;
779            } else {
780                return (internalArray.length - numElements) > contractionCriteria;
781            }
782        }
783    
784        /**
785         * Returns the starting index of the internal array.  The starting index is
786         * the position of the first addressable element in the internal storage
787         * array.  The addressable elements in the array are <code>
788         * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
789         * </code>
790         *
791         * @return starting index
792         */
793        public synchronized int start() {
794            return startIndex;
795        }
796        
797        /**
798         * <p>Copies source to dest, copying the underlying data, so dest is
799         * a new, independent copy of source.  Does not contract before
800         * the copy.</p>
801         * 
802         * <p>Obtains synchronization locks on both source and dest
803         * (in that order) before performing the copy.</p>
804         * 
805         * <p>Neither source nor dest may be null; otherwise a NullPointerException
806         * is thrown</p>
807         * 
808         * @param source ResizableDoubleArray to copy
809         * @param dest ResizableArray to replace with a copy of the source array
810         * @since 2.0
811         * 
812         */
813        public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest) {
814           synchronized(source) {
815               synchronized(dest) {
816                   dest.initialCapacity = source.initialCapacity;
817                   dest.contractionCriteria = source.contractionCriteria;
818                   dest.expansionFactor = source.expansionFactor;
819                   dest.expansionMode = source.expansionMode;
820                   dest.internalArray = new double[source.internalArray.length];
821                   System.arraycopy(source.internalArray, 0, dest.internalArray,
822                           0, dest.internalArray.length);
823                   dest.numElements = source.numElements;
824                   dest.startIndex = source.startIndex;
825               }
826           }
827        }
828        
829        /**
830         * Returns a copy of the ResizableDoubleArray.  Does not contract before
831         * the copy, so the returned object is an exact copy of this.
832         * 
833         * @return a new ResizableDoubleArray with the same data and configuration
834         * properties as this
835         * @since 2.0
836         */
837        public synchronized ResizableDoubleArray copy() {
838            ResizableDoubleArray result = new ResizableDoubleArray();
839            copy(this, result);
840            return result;
841        }
842        
843        /**
844         * Returns true iff object is a ResizableDoubleArray with the same properties
845         * as this and an identical internal storage array.
846         * 
847         * @param object object to be compared for equality with this
848         * @return true iff object is a ResizableDoubleArray with the same data and
849         * properties as this
850         * @since 2.0
851         */
852        @Override
853        public boolean equals(Object object) {
854            if (object == this ) {
855                return true;
856            }
857           if (object instanceof ResizableDoubleArray == false) {
858                return false;
859            }
860           synchronized(this) {
861               synchronized(object) {
862                   boolean result = true;
863                   ResizableDoubleArray other = (ResizableDoubleArray) object;
864                   result = result && (other.initialCapacity == initialCapacity);
865                   result = result && (other.contractionCriteria == contractionCriteria);
866                   result = result && (other.expansionFactor == expansionFactor);
867                   result = result && (other.expansionMode == expansionMode);
868                   result = result && (other.numElements == numElements);
869                   result = result && (other.startIndex == startIndex);
870                   if (!result) { 
871                       return false;
872                   } else {
873                       return Arrays.equals(internalArray, other.internalArray);
874                   }
875               }
876           }
877        }
878        
879        /**
880         * Returns a hash code consistent with equals.
881         * 
882         * @return hash code representing this ResizableDoubleArray
883         * @since 2.0
884         */
885        @Override
886        public synchronized int hashCode() {
887            int[] hashData = new int[7];
888            hashData[0] = new Float(expansionFactor).hashCode();
889            hashData[1] = new Float(contractionCriteria).hashCode();
890            hashData[2] = expansionMode;
891                hashData[3] = Arrays.hashCode(internalArray);
892                hashData[4] = initialCapacity;
893                hashData[5] = numElements;
894                hashData[6] = startIndex;
895            return Arrays.hashCode(hashData);
896        }
897             
898    }