View Javadoc

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