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