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