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