• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package org.apache.commons.math.util;
18 
19 import java.io.Serializable;
20 import java.util.Arrays;
21 
22 import org.apache.commons.math.MathRuntimeException;
23 import org.apache.commons.math.exception.util.LocalizedFormats;
24 
25 /**
26  * <p>
27  * A variable length {@link DoubleArray} implementation that automatically
28  * handles expanding and contracting its internal storage array as elements
29  * are added and removed.
30  * </p>
31  * <p>
32  *  The internal storage array starts with capacity determined by the
33  * <code>initialCapacity</code> property, which can be set by the constructor.
34  * The default initial capacity is 16.  Adding elements using
35  * {@link #addElement(double)} appends elements to the end of the array.  When
36  * there are no open entries at the end of the internal storage array, the
37  * array is expanded.  The size of the expanded array depends on the
38  * <code>expansionMode</code> and <code>expansionFactor</code> properties.
39  * The <code>expansionMode</code> determines whether the size of the array is
40  * multiplied by the <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
41  * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
42  * storage locations added).  The default <code>expansionMode</code> is
43  * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
44  * is 2.0.
45  * </p>
46  * <p>
47  * The {@link #addElementRolling(double)} method adds a new element to the end
48  * of the internal storage array and adjusts the "usable window" of the
49  * internal array forward by one position (effectively making what was the
50  * second element the first, and so on).  Repeated activations of this method
51  * (or activation of {@link #discardFrontElements(int)}) will effectively orphan
52  * the storage locations at the beginning of the internal storage array.  To
53  * reclaim this storage, each time one of these methods is activated, the size
54  * of the internal storage array is compared to the number of addressable
55  * elements (the <code>numElements</code> property) and if the difference
56  * is too large, the internal array is contracted to size
57  * <code>numElements + 1.</code>  The determination of when the internal
58  * storage array is "too large" depends on the <code>expansionMode</code> and
59  * <code>contractionFactor</code> properties.  If  the <code>expansionMode</code>
60  * is <code>MULTIPLICATIVE_MODE</code>, contraction is triggered when the
61  * ratio between storage array length and <code>numElements</code> exceeds
62  * <code>contractionFactor.</code>  If the <code>expansionMode</code>
63  * is <code>ADDITIVE_MODE,</code> the number of excess storage locations
64  * is compared to <code>contractionFactor.</code>
65  * </p>
66  * <p>
67  * To avoid cycles of expansions and contractions, the
68  * <code>expansionFactor</code> must not exceed the
69  * <code>contractionFactor.</code> Constructors and mutators for both of these
70  * properties enforce this requirement, throwing IllegalArgumentException if it
71  * is violated.
72  * </p>
73  * @version $Revision: 1073474 $ $Date: 2011-02-22 20:52:17 +0100 (mar. 22 févr. 2011) $
74  */
75 public class ResizableDoubleArray implements DoubleArray, Serializable {
76 
77     /** additive expansion mode */
78     public static final int ADDITIVE_MODE = 1;
79 
80     /** multiplicative expansion mode */
81     public static final int MULTIPLICATIVE_MODE = 0;
82 
83     /** Serializable version identifier */
84     private static final long serialVersionUID = -3485529955529426875L;
85 
86     /**
87      * The contraction criteria determines when the internal array will be
88      * contracted to fit the number of elements contained in the element
89      *  array + 1.
90      */
91     protected float contractionCriteria = 2.5f;
92 
93     /**
94      * The expansion factor of the array.  When the array needs to be expanded,
95      * the new array size will be
96      * <code>internalArray.length * expansionFactor</code>
97      * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, or
98      * <code>internalArray.length + expansionFactor</code> if
99      * <code>expansionMode</code> is set to ADDITIVE_MODE.
100      */
101     protected float expansionFactor = 2.0f;
102 
103     /**
104      * Determines whether array expansion by <code>expansionFactor</code>
105      * is additive or multiplicative.
106      */
107     protected int expansionMode = MULTIPLICATIVE_MODE;
108 
109     /**
110      * The initial capacity of the array.  Initial capacity is not exposed as a
111      * property as it is only meaningful when passed to a constructor.
112      */
113     protected int initialCapacity = 16;
114 
115     /**
116      * The internal storage array.
117      */
118     protected double[] internalArray;
119 
120     /**
121      * The number of addressable elements in the array.  Note that this
122      * has nothing to do with the length of the internal storage array.
123      */
124     protected int numElements = 0;
125 
126     /**
127      * The position of the first addressable element in the internal storage
128      * array.  The addressable elements in the array are <code>
129      * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
130      * </code>
131      */
132     protected int startIndex = 0;
133 
134     /**
135      * Create a ResizableArray with default properties.
136      * <ul>
137      * <li><code>initialCapacity = 16</code></li>
138      * <li><code>expansionMode = MULTIPLICATIVE_MODE</code></li>
139      * <li><code>expansionFactor = 2.5</code></li>
140      * <li><code>contractionFactor = 2.0</code></li>
141      * </ul>
142      */
ResizableDoubleArray()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      */
ResizableDoubleArray(int initialCapacity)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      */
ResizableDoubleArray(double[] initialArray)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      */
ResizableDoubleArray(int initialCapacity, float expansionFactor)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      */
ResizableDoubleArray(int initialCapacity, float expansionFactor, float contractionCriteria)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      */
ResizableDoubleArray(int initialCapacity, float expansionFactor, float contractionCriteria, int expansionMode)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      */
ResizableDoubleArray(ResizableDoubleArray original)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      */
addElement(double value)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      */
addElements(double[] values)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      */
addElementRolling(double value)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      */
substituteMostRecentElement(double value)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      */
checkContractExpand(float contraction, float expansion)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      */
clear()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      */
contract()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      */
discardFrontElements(int i)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      */
discardMostRecentElements(int i)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      */
discardExtremeElements(int i,boolean front)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      */
expand()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      */
expandTo(int size)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      */
getContractionCriteria()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      */
getElement(int index)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      */
getElements()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      */
getExpansionFactor()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      */
getExpansionMode()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      */
getInternalLength()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      */
getNumElements()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
getValues()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      */
getInternalValues()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      */
setContractionCriteria(float contractionCriteria)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      */
setElement(int index, double value)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      */
setExpansionFactor(float expansionFactor)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      */
setExpansionMode(int expansionMode)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      */
setInitialCapacity(int initialCapacity)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      */
setNumElements(int i)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      */
shouldContract()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      */
start()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      */
copy(ResizableDoubleArray source, ResizableDoubleArray dest)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      */
copy()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
equals(Object object)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
hashCode()924     public synchronized int hashCode() {
925         int[] hashData = new int[7];
926         hashData[0] = new Float(expansionFactor).hashCode();
927         hashData[1] = new Float(contractionCriteria).hashCode();
928         hashData[2] = expansionMode;
929             hashData[3] = Arrays.hashCode(internalArray);
930             hashData[4] = initialCapacity;
931             hashData[5] = numElements;
932             hashData[6] = startIndex;
933         return Arrays.hashCode(hashData);
934     }
935 
936 }
937