ResizableDoubleArray.java

  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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.util;

  22. import java.io.Serializable;
  23. import java.util.Arrays;

  24. import org.hipparchus.exception.LocalizedCoreFormats;
  25. import org.hipparchus.exception.MathIllegalArgumentException;
  26. import org.hipparchus.exception.MathIllegalStateException;
  27. import org.hipparchus.exception.NullArgumentException;

  28. /**
  29.  * A variable length primitive double array implementation that automatically
  30.  * handles expanding and contracting its internal storage array as elements
  31.  * are added and removed.
  32.  * <p>
  33.  * The internal storage array starts with capacity determined by the
  34.  * {@code initialCapacity} property, which can be set by the constructor.
  35.  * The default initial capacity is 16.  Adding elements using
  36.  * {@link #addElement(double)} appends elements to the end of the array.
  37.  * When there are no open entries at the end of the internal storage array,
  38.  * the array is expanded.  The size of the expanded array depends on the
  39.  * {@code expansionMode} and {@code expansionFactor} properties.
  40.  * The {@code expansionMode} determines whether the size of the array is
  41.  * multiplied by the {@code expansionFactor}
  42.  * ({@link ExpansionMode#MULTIPLICATIVE}) or if the expansion is additive
  43.  * ({@link ExpansionMode#ADDITIVE} -- {@code expansionFactor} storage
  44.  * locations added).
  45.  * The default {@code expansionMode} is {@code MULTIPLICATIVE} and the default
  46.  * {@code expansionFactor} is 2.
  47.  * <p>
  48.  * The {@link #addElementRolling(double)} method adds a new element to the end
  49.  * of the internal storage array and adjusts the "usable window" of the
  50.  * internal array forward by one position (effectively making what was the
  51.  * second element the first, and so on).  Repeated activations of this method
  52.  * (or activation of {@link #discardFrontElements(int)}) will effectively orphan
  53.  * the storage locations at the beginning of the internal storage array.  To
  54.  * reclaim this storage, each time one of these methods is activated, the size
  55.  * of the internal storage array is compared to the number of addressable
  56.  * elements (the {@code numElements} property) and if the difference
  57.  * is too large, the internal array is contracted to size
  58.  * {@code numElements + 1}.  The determination of when the internal
  59.  * storage array is "too large" depends on the {@code expansionMode} and
  60.  * {@code contractionFactor} properties.  If  the {@code expansionMode}
  61.  * is {@code MULTIPLICATIVE}, contraction is triggered when the
  62.  * ratio between storage array length and {@code numElements} exceeds
  63.  * {@code contractionFactor.}  If the {@code expansionMode}
  64.  * is {@code ADDITIVE}, the number of excess storage locations
  65.  * is compared to {@code contractionFactor}.
  66.  * <p>
  67.  * To avoid cycles of expansions and contractions, the
  68.  * {@code expansionFactor} must not exceed the {@code contractionFactor}.
  69.  * Constructors and mutators for both of these properties enforce this
  70.  * requirement, throwing a {@code MathIllegalArgumentException} if it is
  71.  * violated.
  72.  * <p>
  73.  * <b>Note:</b> this class is <b>NOT</b> thread-safe.
  74.  */
  75. public class ResizableDoubleArray implements Serializable {
  76.     /** Serializable version identifier. */
  77.     private static final long serialVersionUID = 20160327L;

  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.      * The contraction criteria determines when the internal array will be
  91.      * contracted to fit the number of elements contained in the element
  92.      * array + 1.
  93.      */
  94.     private final double contractionCriterion;

  95.     /**
  96.      * The expansion factor of the array.  When the array needs to be expanded,
  97.      * the new array size will be {@code internalArray.length * expansionFactor}
  98.      * if {@code expansionMode} is set to MULTIPLICATIVE, or
  99.      * {@code internalArray.length + expansionFactor} if
  100.      * {@code expansionMode} is set to ADDITIVE.
  101.      */
  102.     private final double expansionFactor;

  103.     /**
  104.      * Determines whether array expansion by {@code expansionFactor}
  105.      * is additive or multiplicative.
  106.      */
  107.     private final ExpansionMode expansionMode;

  108.     /**
  109.      * The internal storage array.
  110.      */
  111.     private double[] internalArray;

  112.     /**
  113.      * The number of addressable elements in the array.  Note that this
  114.      * has nothing to do with the length of the internal storage array.
  115.      */
  116.     private int numElements;

  117.     /**
  118.      * The position of the first addressable element in the internal storage
  119.      * array.  The addressable elements in the array are
  120.      * {@code internalArray[startIndex],...,internalArray[startIndex + numElements - 1]}.
  121.      */
  122.     private int startIndex;

  123.     /** Specification of expansion algorithm. */
  124.     public enum ExpansionMode {
  125.         /** Multiplicative expansion mode. */
  126.         MULTIPLICATIVE,
  127.         /** Additive expansion mode. */
  128.         ADDITIVE
  129.     }

  130.     /**
  131.      * Creates an instance with default properties.
  132.      * <ul>
  133.      *  <li>{@code initialCapacity = 16}</li>
  134.      *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
  135.      *  <li>{@code expansionFactor = 2.0}</li>
  136.      *  <li>{@code contractionCriterion = 2.5}</li>
  137.      * </ul>
  138.      */
  139.     public ResizableDoubleArray() {
  140.         this(DEFAULT_INITIAL_CAPACITY);
  141.     }

  142.     /**
  143.      * Creates an instance with the specified initial capacity.
  144.      * <p>
  145.      * Other properties take default values:
  146.      * <ul>
  147.      *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
  148.      *  <li>{@code expansionFactor = 2.0}</li>
  149.      *  <li>{@code contractionCriterion = 2.5}</li>
  150.      * </ul>
  151.      * @param initialCapacity Initial size of the internal storage array.
  152.      * @throws MathIllegalArgumentException if {@code initialCapacity <= 0}.
  153.      */
  154.     public ResizableDoubleArray(int initialCapacity) throws MathIllegalArgumentException {
  155.         this(initialCapacity, DEFAULT_EXPANSION_FACTOR);
  156.     }

  157.     /**
  158.      * Creates an instance from an existing {@code double[]} with the
  159.      * initial capacity and numElements corresponding to the size of
  160.      * the supplied {@code double[]} array.
  161.      * <p>
  162.      * If the supplied array is null, a new empty array with the default
  163.      * initial capacity will be created.
  164.      * The input array is copied, not referenced.
  165.      * Other properties take default values:
  166.      * <ul>
  167.      *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
  168.      *  <li>{@code expansionFactor = 2.0}</li>
  169.      *  <li>{@code contractionCriterion = 2.5}</li>
  170.      * </ul>
  171.      *
  172.      * @param initialArray initial array
  173.      */
  174.     public ResizableDoubleArray(double[] initialArray) {
  175.         this(initialArray == null || initialArray.length == 0 ?
  176.              DEFAULT_INITIAL_CAPACITY : initialArray.length,
  177.              DEFAULT_EXPANSION_FACTOR,
  178.              DEFAULT_CONTRACTION_DELTA + DEFAULT_EXPANSION_FACTOR,
  179.              DEFAULT_EXPANSION_MODE,
  180.              initialArray);
  181.     }

  182.     /**
  183.      * Creates an instance with the specified initial capacity
  184.      * and expansion factor.
  185.      * <p>
  186.      * The remaining properties take default values:
  187.      * <ul>
  188.      *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
  189.      *  <li>{@code contractionCriterion = 0.5 + expansionFactor}</li>
  190.      * </ul>
  191.      * <p>
  192.      * Throws MathIllegalArgumentException if the following conditions
  193.      * are not met:
  194.      * <ul>
  195.      *  <li>{@code initialCapacity > 0}</li>
  196.      *  <li>{@code expansionFactor > 1}</li>
  197.      * </ul>
  198.      *
  199.      * @param initialCapacity Initial size of the internal storage array.
  200.      * @param expansionFactor The array will be expanded based on this parameter.
  201.      * @throws MathIllegalArgumentException if parameters are not valid.
  202.      */
  203.     public ResizableDoubleArray(int initialCapacity, double expansionFactor) throws MathIllegalArgumentException {
  204.         this(initialCapacity, expansionFactor, DEFAULT_CONTRACTION_DELTA + expansionFactor);
  205.     }

  206.     /**
  207.      * Creates an instance with the specified initial capacity,
  208.      * expansion factor, and contraction criteria.
  209.      * <p>
  210.      * The expansion mode will default to {@code MULTIPLICATIVE}.
  211.      * <p>
  212.      * Throws MathIllegalArgumentException if the following conditions
  213.      * are not met:
  214.      * <ul>
  215.      *  <li>{@code initialCapacity > 0}</li>
  216.      *  <li>{@code expansionFactor > 1}</li>
  217.      *  <li>{@code contractionCriterion >= expansionFactor}</li>
  218.      * </ul>
  219.      *
  220.      * @param initialCapacity Initial size of the internal storage array.
  221.      * @param expansionFactor The array will be expanded based on this parameter.
  222.      * @param contractionCriterion Contraction criterion.
  223.      * @throws MathIllegalArgumentException if the parameters are not valid.
  224.      */
  225.     public ResizableDoubleArray(int initialCapacity, double expansionFactor, double contractionCriterion)
  226.         throws MathIllegalArgumentException {
  227.         this(initialCapacity, expansionFactor, contractionCriterion, DEFAULT_EXPANSION_MODE, null);
  228.     }

  229.     /**
  230.      * Creates an instance with the specified properties.
  231.      * <br>
  232.      * Throws MathIllegalArgumentException if the following conditions
  233.      * are not met:
  234.      * <ul>
  235.      *  <li>{@code initialCapacity > 0}</li>
  236.      *  <li>{@code expansionFactor > 1}</li>
  237.      *  <li>{@code contractionCriterion >= expansionFactor}</li>
  238.      * </ul>
  239.      *
  240.      * @param initialCapacity Initial size of the internal storage array.
  241.      * @param expansionFactor The array will be expanded based on this parameter.
  242.      * @param contractionCriterion Contraction criteria.
  243.      * @param expansionMode Expansion mode.
  244.      * @param data Initial contents of the array.
  245.      * @throws MathIllegalArgumentException if the parameters are not valid.
  246.      * @throws NullArgumentException if expansionMode is null
  247.      */
  248.     public ResizableDoubleArray(int initialCapacity,
  249.                                 double expansionFactor,
  250.                                 double contractionCriterion,
  251.                                 ExpansionMode expansionMode,
  252.                                 double ... data)
  253.         throws MathIllegalArgumentException {
  254.         if (initialCapacity <= 0) {
  255.             throw new MathIllegalArgumentException(LocalizedCoreFormats.INITIAL_CAPACITY_NOT_POSITIVE,
  256.                                                    initialCapacity);
  257.         }
  258.         checkContractExpand(contractionCriterion, expansionFactor);
  259.         MathUtils.checkNotNull(expansionMode);

  260.         this.expansionFactor = expansionFactor;
  261.         this.contractionCriterion = contractionCriterion;
  262.         this.expansionMode = expansionMode;
  263.         internalArray = new double[initialCapacity];
  264.         numElements = 0;
  265.         startIndex = 0;

  266.         if (data != null && data.length > 0) {
  267.             addElements(data);
  268.         }
  269.     }

  270.     /**
  271.      * Copy constructor.
  272.      * <p>
  273.      * Creates a new ResizableDoubleArray that is a deep, fresh copy of the original.
  274.      * Original may not be null; otherwise a {@link NullArgumentException} is thrown.
  275.      *
  276.      * @param original array to copy
  277.      * @exception NullArgumentException if original is null
  278.      */
  279.     public ResizableDoubleArray(final ResizableDoubleArray original)
  280.         throws NullArgumentException {
  281.         MathUtils.checkNotNull(original);
  282.         this.contractionCriterion = original.contractionCriterion;
  283.         this.expansionFactor = original.expansionFactor;
  284.         this.expansionMode = original.expansionMode;
  285.         this.internalArray = new double[original.internalArray.length];
  286.         System.arraycopy(original.internalArray, 0, this.internalArray, 0, this.internalArray.length);
  287.         this.numElements = original.numElements;
  288.         this.startIndex = original.startIndex;
  289.     }

  290.     /**
  291.      * Adds an element to the end of this expandable array.
  292.      *
  293.      * @param value Value to be added to end of array.
  294.      */
  295.     public void addElement(final double value) {
  296.         if (internalArray.length <= startIndex + numElements) {
  297.             expand();
  298.         }
  299.         internalArray[startIndex + numElements++] = value;
  300.     }

  301.     /**
  302.      * Adds several element to the end of this expandable array.
  303.      *
  304.      * @param values Values to be added to end of array.
  305.      */
  306.     public void addElements(final double[] values) {
  307.         final double[] tempArray = new double[numElements + values.length + 1];
  308.         System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
  309.         System.arraycopy(values, 0, tempArray, numElements, values.length);
  310.         internalArray = tempArray;
  311.         startIndex = 0;
  312.         numElements += values.length;
  313.     }

  314.     /**
  315.      * Adds an element to the end of the array and removes the first
  316.      * element in the array.  Returns the discarded first element.
  317.      * <p>
  318.      * The effect is similar to a push operation in a FIFO queue.
  319.      * <p>
  320.      * Example: If the array contains the elements 1, 2, 3, 4 (in that order)
  321.      * and addElementRolling(5) is invoked, the result is an array containing
  322.      * the entries 2, 3, 4, 5 and the value returned is 1.
  323.      *
  324.      * @param value Value to be added to the array.
  325.      * @return the value which has been discarded or "pushed" out of the array
  326.      * by this rolling insert.
  327.      */
  328.     public double addElementRolling(double value) {
  329.         double discarded = internalArray[startIndex];

  330.         if ((startIndex + (numElements + 1)) > internalArray.length) {
  331.             expand();
  332.         }
  333.         // Increment the start index
  334.         startIndex += 1;

  335.         // Add the new value
  336.         internalArray[startIndex + (numElements - 1)] = value;

  337.         // Check the contraction criterion.
  338.         if (shouldContract()) {
  339.             contract();
  340.         }
  341.         return discarded;
  342.     }

  343.     /**
  344.      * Substitutes {@code value} for the most recently added value.
  345.      * <p>
  346.      * Returns the value that has been replaced. If the array is empty (i.e.
  347.      * if {@link #numElements} is zero), an MathIllegalStateException is thrown.
  348.      *
  349.      * @param value New value to substitute for the most recently added value
  350.      * @return the value that has been replaced in the array.
  351.      * @throws MathIllegalStateException if the array is empty
  352.      */
  353.     public double substituteMostRecentElement(double value) throws MathIllegalStateException {
  354.         if (numElements < 1) {
  355.             throw new MathIllegalStateException(LocalizedCoreFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY);
  356.         }

  357.         final int substIndex = startIndex + (numElements - 1);
  358.         final double discarded = internalArray[substIndex];

  359.         internalArray[substIndex] = value;

  360.         return discarded;
  361.     }

  362.     /**
  363.      * Checks the expansion factor and the contraction criterion and raises
  364.      * an exception if the contraction criterion is smaller than the
  365.      * expansion criterion.
  366.      *
  367.      * @param contraction Criterion to be checked.
  368.      * @param expansion Factor to be checked.
  369.      * @throws MathIllegalArgumentException if {@code contraction < expansion}.
  370.      * @throws MathIllegalArgumentException if {@code contraction <= 1}.
  371.      * @throws MathIllegalArgumentException if {@code expansion <= 1 }.
  372.      */
  373.     protected void checkContractExpand(double contraction, double expansion)
  374.         throws MathIllegalArgumentException {

  375.         if (contraction < expansion) {
  376.             throw new MathIllegalArgumentException(LocalizedCoreFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR,
  377.                                                    contraction, expansion);
  378.         }

  379.         if (contraction <= 1) {
  380.             throw new MathIllegalArgumentException(LocalizedCoreFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE,
  381.                                                    contraction);
  382.         }

  383.         if (expansion <= 1) {
  384.             throw new MathIllegalArgumentException(LocalizedCoreFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE,
  385.                                                    expansion);
  386.         }
  387.     }

  388.     /**
  389.      * Clear the array contents, resetting the number of elements to zero.
  390.      */
  391.     public void clear() {
  392.         numElements = 0;
  393.         startIndex = 0;
  394.     }

  395.     /**
  396.      * Contracts the storage array to the (size of the element set) + 1 - to avoid
  397.      * a zero length array. This function also resets the startIndex to zero.
  398.      */
  399.     public void contract() {
  400.         final double[] tempArray = new double[numElements + 1];

  401.         // Copy and swap - copy only the element array from the src array.
  402.         System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
  403.         internalArray = tempArray;

  404.         // Reset the start index to zero
  405.         startIndex = 0;
  406.     }

  407.     /**
  408.      * Discards the {@code i} initial elements of the array.
  409.      * <p>
  410.      * For example, if the array contains the elements 1,2,3,4, invoking
  411.      * {@code discardFrontElements(2)} will cause the first two elements
  412.      * to be discarded, leaving 3,4 in the array.
  413.      *
  414.      * @param i  the number of elements to discard from the front of the array
  415.      * @throws MathIllegalArgumentException if i is greater than numElements.
  416.      */
  417.     public void discardFrontElements(int i) throws MathIllegalArgumentException {
  418.         discardExtremeElements(i,true);
  419.     }

  420.     /**
  421.      * Discards the {@code i} last elements of the array.
  422.      * <p>
  423.      * For example, if the array contains the elements 1,2,3,4, invoking
  424.      * {@code discardMostRecentElements(2)} will cause the last two elements
  425.      * to be discarded, leaving 1,2 in the array.
  426.      *
  427.      * @param i  the number of elements to discard from the end of the array
  428.      * @throws MathIllegalArgumentException if i is greater than numElements.
  429.      */
  430.     public void discardMostRecentElements(int i) throws MathIllegalArgumentException {
  431.         discardExtremeElements(i,false);
  432.     }

  433.     /**
  434.      * Discards the {@code i} first or last elements of the array,
  435.      * depending on the value of {@code front}.
  436.      * <p>
  437.      * For example, if the array contains the elements 1,2,3,4, invoking
  438.      * {@code discardExtremeElements(2,false)} will cause the last two elements
  439.      * to be discarded, leaving 1,2 in the array.
  440.      * For example, if the array contains the elements 1,2,3,4, invoking
  441.      * {@code discardExtremeElements(2,true)} will cause the first two elements
  442.      * to be discarded, leaving 3,4 in the array.
  443.      *
  444.      * @param i  the number of elements to discard from the front/end of the array
  445.      * @param front true if elements are to be discarded from the front
  446.      * of the array, false if elements are to be discarded from the end
  447.      * of the array
  448.      * @throws MathIllegalArgumentException if i is greater than numElements.
  449.      */
  450.     private void discardExtremeElements(int i, boolean front) throws MathIllegalArgumentException {
  451.         if (i > numElements) {
  452.             throw new MathIllegalArgumentException(
  453.                     LocalizedCoreFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY,
  454.                     i, numElements);
  455.        } else if (i < 0) {
  456.            throw new MathIllegalArgumentException(
  457.                    LocalizedCoreFormats.CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS,
  458.                    i);
  459.         } else {
  460.             // "Subtract" this number of discarded from numElements
  461.             numElements -= i;
  462.             if (front) {
  463.                 startIndex += i;
  464.             }
  465.         }
  466.         if (shouldContract()) {
  467.             contract();
  468.         }
  469.     }

  470.     /**
  471.      * Expands the internal storage array using the expansion factor.
  472.      * <p>
  473.      * If {@code expansionMode} is set to MULTIPLICATIVE,
  474.      * the new array size will be {@code internalArray.length * expansionFactor}.
  475.      * If {@code expansionMode} is set to ADDITIVE, the length
  476.      * after expansion will be {@code internalArray.length + expansionFactor}.
  477.      */
  478.     protected void expand() {
  479.         // notice the use of FastMath.ceil(), this guarantees that we will always
  480.         // have an array of at least currentSize + 1.   Assume that the
  481.         // current initial capacity is 1 and the expansion factor
  482.         // is 1.000000000000000001.  The newly calculated size will be
  483.         // rounded up to 2 after the multiplication is performed.
  484.         final int newSize;
  485.         if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
  486.             newSize = (int) FastMath.ceil(internalArray.length * expansionFactor);
  487.         } else {
  488.             newSize = (int) (internalArray.length + FastMath.round(expansionFactor));
  489.         }
  490.         final double[] tempArray = new double[newSize];

  491.         // Copy and swap
  492.         System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
  493.         internalArray = tempArray;
  494.     }

  495.     /**
  496.      * Expands the internal storage array to the specified size.
  497.      *
  498.      * @param size Size of the new internal storage array.
  499.      */
  500.     private void expandTo(int size) {
  501.         final double[] tempArray = new double[size];
  502.         // Copy and swap
  503.         System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
  504.         internalArray = tempArray;
  505.     }

  506.     /**
  507.      * The contraction criterion defines when the internal array will contract
  508.      * to store only the number of elements in the element array.
  509.      * <p>
  510.      * If the {@code expansionMode} is {@code MULTIPLICATIVE},
  511.      * contraction is triggered when the ratio between storage array length
  512.      * and {@code numElements} exceeds {@code contractionFactor}.
  513.      * If the {@code expansionMode} is {@code ADDITIVE}, the
  514.      * number of excess storage locations is compared to {@code contractionFactor}.
  515.      *
  516.      * @return the contraction criterion used to reclaim memory.
  517.      */
  518.     public double getContractionCriterion() {
  519.         return contractionCriterion;
  520.     }

  521.     /**
  522.      * Returns the element at the specified index.
  523.      *
  524.      * @param index index to fetch a value from
  525.      * @return value stored at the specified index
  526.      * @throws ArrayIndexOutOfBoundsException if {@code index} is less than
  527.      * zero or is greater than {@code getNumElements() - 1}.
  528.      */
  529.     public double getElement(int index) {
  530.         if (index >= numElements) {
  531.             throw new ArrayIndexOutOfBoundsException(index);
  532.         } else if (index >= 0) {
  533.             return internalArray[startIndex + index];
  534.         } else {
  535.             throw new ArrayIndexOutOfBoundsException(index);
  536.         }
  537.     }

  538.      /**
  539.      * Returns a double array containing the elements of this ResizableArray.
  540.      * <p>
  541.      * This method returns a copy, not a reference to the underlying array,
  542.      * so that changes made to the returned array have no effect on this ResizableArray.
  543.      *
  544.      * @return the double array.
  545.      */
  546.     public double[] getElements() {
  547.         final double[] elementArray = new double[numElements];
  548.         System.arraycopy(internalArray, startIndex, elementArray, 0, numElements);
  549.         return elementArray;
  550.     }

  551.     /**
  552.      * The expansion factor controls the size of a new array when an array
  553.      * needs to be expanded.
  554.      * <p>
  555.      * The {@code expansionMode} determines whether the size of the array
  556.      * is multiplied by the {@code expansionFactor} (MULTIPLICATIVE) or if
  557.      * the expansion is additive (ADDITIVE -- {@code expansionFactor}
  558.      * storage locations added).  The default {@code expansionMode} is
  559.      * MULTIPLICATIVE and the default {@code expansionFactor} is 2.0.
  560.      *
  561.      * @return the expansion factor of this expandable double array
  562.      */
  563.     public double getExpansionFactor() {
  564.         return expansionFactor;
  565.     }

  566.     /**
  567.      * The expansion mode determines whether the internal storage
  568.      * array grows additively or multiplicatively when it is expanded.
  569.      *
  570.      * @return the expansion mode.
  571.      */
  572.     public ExpansionMode getExpansionMode() {
  573.         return expansionMode;
  574.     }

  575.     /**
  576.      * Gets the currently allocated size of the internal data structure used
  577.      * for storing elements.
  578.      * This is not to be confused with {@link #getNumElements() the number of
  579.      * elements actually stored}.
  580.      *
  581.      * @return the length of the internal array.
  582.      */
  583.     public int getCapacity() {
  584.         return internalArray.length;
  585.     }

  586.     /**
  587.      * Returns the number of elements currently in the array.  Please note
  588.      * that this is different from the length of the internal storage array.
  589.      *
  590.      * @return the number of elements.
  591.      */
  592.     public int getNumElements() {
  593.         return numElements;
  594.     }

  595.     /**
  596.      * Provides <em>direct</em> access to the internal storage array.
  597.      * Please note that this method returns a reference to this object's
  598.      * storage array, not a copy.
  599.      * <p>
  600.      * To correctly address elements of the array, the "start index" is
  601.      * required (available via the {@link #getStartIndex() getStartIndex}
  602.      * method.
  603.      * <p>
  604.      * This method should only be used to avoid copying the internal array.
  605.      * The returned value <em>must</em> be used for reading only; other
  606.      * uses could lead to this object becoming inconsistent.
  607.      * <p>
  608.      * The {@link #getElements} method has no such limitation since it
  609.      * returns a copy of this array's addressable elements.
  610.      *
  611.      * @return the internal storage array used by this object.
  612.      */
  613.     protected double[] getArrayRef() {
  614.         return internalArray; // NOPMD - returning an internal array is intentional and documented here
  615.     }

  616.     /**
  617.      * Returns the "start index" of the internal array.
  618.      * This index is the position of the first addressable element in the
  619.      * internal storage array.
  620.      * <p>
  621.      * The addressable elements in the array are at indices contained in
  622.      * the interval [{@link #getStartIndex()},
  623.      *               {@link #getStartIndex()} + {@link #getNumElements()} - 1].
  624.      *
  625.      * @return the start index.
  626.      */
  627.     protected int getStartIndex() {
  628.         return startIndex;
  629.     }

  630.     /**
  631.      * Performs an operation on the addressable elements of the array.
  632.      *
  633.      * @param f Function to be applied on this array.
  634.      * @return the result.
  635.      */
  636.     public double compute(MathArrays.Function f) {
  637.         return f.evaluate(internalArray, startIndex, numElements);
  638.     }

  639.     /**
  640.      * Sets the element at the specified index.
  641.      * <p>
  642.      * If the specified index is greater than {@code getNumElements() - 1},
  643.      * the {@code numElements} property is increased to {@code index +1}
  644.      * and additional storage is allocated (if necessary) for the new element and
  645.      * all (uninitialized) elements between the new element and the previous end
  646.      * of the array).
  647.      *
  648.      * @param index index to store a value in
  649.      * @param value value to store at the specified index
  650.      * @throws ArrayIndexOutOfBoundsException if {@code index < 0}.
  651.      */
  652.     public void setElement(int index, double value) {
  653.         if (index < 0) {
  654.             throw new ArrayIndexOutOfBoundsException(index);
  655.         }
  656.         if (index + 1 > numElements) {
  657.             numElements = index + 1;
  658.         }
  659.         if ((startIndex + index) >= internalArray.length) {
  660.             expandTo(startIndex + (index + 1));
  661.         }
  662.         internalArray[startIndex + index] = value;
  663.     }

  664.     /**
  665.      * This function allows you to control the number of elements contained
  666.      * in this array, and can be used to "throw out" the last n values in an
  667.      * array. This function will also expand the internal array as needed.
  668.      *
  669.      * @param i a new number of elements
  670.      * @throws MathIllegalArgumentException if {@code i} is negative.
  671.      */
  672.     public void setNumElements(int i) throws MathIllegalArgumentException {
  673.         // If index is negative thrown an error.
  674.         if (i < 0) {
  675.             throw new MathIllegalArgumentException(LocalizedCoreFormats.INDEX_NOT_POSITIVE, i);
  676.         }

  677.         // Test the new num elements, check to see if the array needs to be
  678.         // expanded to accommodate this new number of elements.
  679.         final int newSize = startIndex + i;
  680.         if (newSize > internalArray.length) {
  681.             expandTo(newSize);
  682.         }

  683.         // Set the new number of elements to new value.
  684.         numElements = i;
  685.     }

  686.     /**
  687.      * Returns true if the internal storage array has too many unused
  688.      * storage positions.
  689.      *
  690.      * @return true if array satisfies the contraction criteria
  691.      */
  692.     private boolean shouldContract() {
  693.         if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
  694.             return (internalArray.length / ((float) numElements)) > contractionCriterion;
  695.         } else {
  696.             return (internalArray.length - numElements) > contractionCriterion;
  697.         }
  698.     }

  699.     /**
  700.      * Returns a copy of the ResizableDoubleArray.  Does not contract before
  701.      * the copy, so the returned object is an exact copy of this.
  702.      *
  703.      * @return a new ResizableDoubleArray with the same data and configuration
  704.      * properties as this
  705.      */
  706.     public ResizableDoubleArray copy() {
  707.         return new ResizableDoubleArray(this);
  708.     }

  709.     /**
  710.      * Returns true iff object is a ResizableDoubleArray with the same properties
  711.      * as this and an identical internal storage array.
  712.      *
  713.      * @param object object to be compared for equality with this
  714.      * @return true iff object is a ResizableDoubleArray with the same data and
  715.      * properties as this
  716.      */
  717.     @Override
  718.     public boolean equals(Object object) {
  719.         if (object == this) {
  720.             return true;
  721.         }
  722.         if (!(object instanceof ResizableDoubleArray)) {
  723.             return false;
  724.         }
  725.         boolean result = true;
  726.         final ResizableDoubleArray other = (ResizableDoubleArray) object;
  727.         result = result && (other.contractionCriterion == contractionCriterion);
  728.         result = result && (other.expansionFactor == expansionFactor);
  729.         result = result && (other.expansionMode == expansionMode);
  730.         result = result && (other.numElements == numElements);
  731.         result = result && (other.startIndex == startIndex);
  732.         if (!result) {
  733.             return false;
  734.         } else {
  735.             return Arrays.equals(internalArray, other.internalArray);
  736.         }
  737.     }

  738.     /**
  739.      * Returns a hash code consistent with equals.
  740.      *
  741.      * @return the hash code representing this {@code ResizableDoubleArray}.
  742.      */
  743.     @Override
  744.     public int hashCode() {
  745.         final int[] hashData = new int[6];
  746.         hashData[0] = Double.valueOf(expansionFactor).hashCode();
  747.         hashData[1] = Double.valueOf(contractionCriterion).hashCode();
  748.         hashData[2] = expansionMode.hashCode();
  749.         hashData[3] = Arrays.hashCode(internalArray);
  750.         hashData[4] = numElements;
  751.         hashData[5] = startIndex;
  752.         return Arrays.hashCode(hashData);
  753.     }

  754. }