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