View Javadoc
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 }