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