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