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.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.math4.legacy.stat.descriptive;

  18. import java.util.Arrays;

  19. import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
  20. import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
  21. import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
  22. import org.apache.commons.math4.legacy.exception.NullArgumentException;
  23. import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
  24. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
  25. import org.apache.commons.math4.core.jdkmath.JdkMath;
  26. import org.apache.commons.math4.legacy.core.MathArrays;

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

  86.     /**
  87.      * The contraction criteria determines when the internal array will be
  88.      * contracted to fit the number of elements contained in the element
  89.      *  array + 1.
  90.      */
  91.     private final double contractionCriterion;

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

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

  105.     /**
  106.      * The internal storage array.
  107.      */
  108.     private double[] internalArray;

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

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

  120.     /**
  121.      * Specification of expansion algorithm.
  122.      * @since 3.1
  123.      */
  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.     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.     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.      * @since 2.2
  174.      */
  175.     ResizableDoubleArray(double[] initialArray) {
  176.         this(initialArray == null || initialArray.length == 0 ?
  177.               DEFAULT_INITIAL_CAPACITY :
  178.               initialArray.length,
  179.              DEFAULT_EXPANSION_FACTOR,
  180.              DEFAULT_CONTRACTION_DELTA + DEFAULT_EXPANSION_FACTOR,
  181.              DEFAULT_EXPANSION_MODE,
  182.              initialArray);
  183.     }

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

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

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

  264.         this.expansionFactor = expansionFactor;
  265.         this.contractionCriterion = contractionCriterion;
  266.         this.expansionMode = expansionMode;
  267.         internalArray = new double[initialCapacity];
  268.         numElements = 0;
  269.         startIndex = 0;

  270.         if (data != null && data.length > 0) {
  271.             addElements(data);
  272.         }
  273.     }

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

  295.     /**
  296.      * Adds an element to the end of this expandable array.
  297.      *
  298.      * @param value Value to be added to end of array.
  299.      */
  300.     @Override
  301.     public void addElement(final double value) {
  302.         if (internalArray.length <= startIndex + numElements) {
  303.             expand();
  304.         }
  305.         internalArray[startIndex + numElements++] = value;
  306.     }

  307.     /**
  308.      * Adds several element to the end of this expandable array.
  309.      *
  310.      * @param values Values to be added to end of array.
  311.      * @since 2.2
  312.      */
  313.     @Override
  314.     public void addElements(final double[] values) {
  315.         final double[] tempArray = new double[numElements + values.length + 1];
  316.         System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
  317.         System.arraycopy(values, 0, tempArray, numElements, values.length);
  318.         internalArray = tempArray;
  319.         startIndex = 0;
  320.         numElements += values.length;
  321.     }

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

  339.         if ((startIndex + (numElements + 1)) > internalArray.length) {
  340.             expand();
  341.         }
  342.         // Increment the start index
  343.         startIndex += 1;

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

  346.         // Check the contraction criterion.
  347.         if (shouldContract()) {
  348.             contract();
  349.         }
  350.         return discarded;
  351.     }

  352.     /**
  353.      * Substitutes {@code value} for the most recently added value.
  354.      * <p>
  355.      * Returns the value that has been replaced. If the array is empty (i.e.
  356.      * if {@link #numElements} is zero), an MathIllegalStateException is thrown.
  357.      *
  358.      * @param value New value to substitute for the most recently added value
  359.      * @return the value that has been replaced in the array.
  360.      * @throws MathIllegalStateException if the array is empty
  361.      * @since 2.0
  362.      */
  363.     public double substituteMostRecentElement(double value) throws MathIllegalStateException {
  364.         if (numElements < 1) {
  365.             throw new MathIllegalStateException(LocalizedFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY);
  366.         }

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

  369.         internalArray[substIndex] = value;

  370.         return discarded;
  371.     }

  372.     /**
  373.      * Checks the expansion factor and the contraction criterion and raises
  374.      * an exception if the contraction criterion is smaller than the
  375.      * expansion criterion.
  376.      *
  377.      * @param contraction Criterion to be checked.
  378.      * @param expansion Factor to be checked.
  379.      * @throws NumberIsTooSmallException if {@code contraction < expansion}.
  380.      * @throws NumberIsTooSmallException if {@code contraction <= 1}.
  381.      * @throws NumberIsTooSmallException if {@code expansion <= 1 }.
  382.      * @since 3.1
  383.      */
  384.     protected void checkContractExpand(double contraction, double expansion) throws NumberIsTooSmallException {
  385.         if (contraction < expansion) {
  386.             final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, true);
  387.             e.getContext().addMessage(LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR,
  388.                                       contraction, expansion);
  389.             throw e;
  390.         }

  391.         if (contraction <= 1) {
  392.             final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, false);
  393.             e.getContext().addMessage(LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE,
  394.                                       contraction);
  395.             throw e;
  396.         }

  397.         if (expansion <= 1) {
  398.             final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, false);
  399.             e.getContext().addMessage(LocalizedFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE,
  400.                                       expansion);
  401.             throw e;
  402.         }
  403.     }

  404.     /**
  405.      * Clear the array contents, resetting the number of elements to zero.
  406.      */
  407.     @Override
  408.     public void clear() {
  409.         numElements = 0;
  410.         startIndex = 0;
  411.     }

  412.     /**
  413.      * Contracts the storage array to the (size of the element set) + 1 - to avoid
  414.      * a zero length array. This function also resets the startIndex to zero.
  415.      */
  416.     public void contract() {
  417.         final double[] tempArray = new double[numElements + 1];

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

  421.         // Reset the start index to zero
  422.         startIndex = 0;
  423.     }

  424.     /**
  425.      * Discards the {@code i} initial elements of the array.
  426.      * <p>
  427.      * For example, if the array contains the elements 1,2,3,4, invoking
  428.      * {@code discardFrontElements(2)} will cause the first two elements
  429.      * to be discarded, leaving 3,4 in the array.
  430.      *
  431.      * @param i  the number of elements to discard from the front of the array
  432.      * @throws MathIllegalArgumentException if i is greater than numElements.
  433.      * @since 2.0
  434.      */
  435.     public void discardFrontElements(int i) throws MathIllegalArgumentException {
  436.         discardExtremeElements(i,true);
  437.     }

  438.     /**
  439.      * Discards the {@code i} last elements of the array.
  440.      * <p>
  441.      * For example, if the array contains the elements 1,2,3,4, invoking
  442.      * {@code discardMostRecentElements(2)} will cause the last two elements
  443.      * to be discarded, leaving 1,2 in the array.
  444.      *
  445.      * @param i  the number of elements to discard from the end of the array
  446.      * @throws MathIllegalArgumentException if i is greater than numElements.
  447.      * @since 2.0
  448.      */
  449.     public void discardMostRecentElements(int i) throws MathIllegalArgumentException {
  450.         discardExtremeElements(i,false);
  451.     }

  452.     /**
  453.      * Discards the {@code i} first or last elements of the array,
  454.      * depending on the value of {@code front}.
  455.      * <p>
  456.      * For example, if the array contains the elements 1,2,3,4, invoking
  457.      * {@code discardExtremeElements(2,false)} will cause the last two elements
  458.      * to be discarded, leaving 1,2 in the array.
  459.      * For example, if the array contains the elements 1,2,3,4, invoking
  460.      * {@code discardExtremeElements(2,true)} will cause the first two elements
  461.      * to be discarded, leaving 3,4 in the array.
  462.      *
  463.      * @param i  the number of elements to discard from the front/end of the array
  464.      * @param front true if elements are to be discarded from the front
  465.      * of the array, false if elements are to be discarded from the end
  466.      * of the array
  467.      * @throws MathIllegalArgumentException if i is greater than numElements.
  468.      * @since 2.0
  469.      */
  470.     private void discardExtremeElements(int i, boolean front) throws MathIllegalArgumentException {
  471.         if (i > numElements) {
  472.             throw new MathIllegalArgumentException(
  473.                     LocalizedFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY,
  474.                     i, numElements);
  475.        } else if (i < 0) {
  476.            throw new MathIllegalArgumentException(
  477.                    LocalizedFormats.CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS,
  478.                    i);
  479.         } else {
  480.             // "Subtract" this number of discarded from numElements
  481.             numElements -= i;
  482.             if (front) {
  483.                 startIndex += i;
  484.             }
  485.         }
  486.         if (shouldContract()) {
  487.             contract();
  488.         }
  489.     }

  490.     /**
  491.      * Expands the internal storage array using the expansion factor.
  492.      * <p>
  493.      * If {@code expansionMode} is set to MULTIPLICATIVE,
  494.      * the new array size will be {@code internalArray.length * expansionFactor}.
  495.      * If {@code expansionMode} is set to ADDITIVE, the length
  496.      * after expansion will be {@code internalArray.length + expansionFactor}.
  497.      */
  498.     protected void expand() {
  499.         // notice the use of JdkMath.ceil(), this guarantees that we will always
  500.         // have an array of at least currentSize + 1.   Assume that the
  501.         // current initial capacity is 1 and the expansion factor
  502.         // is 1.000000000000000001.  The newly calculated size will be
  503.         // rounded up to 2 after the multiplication is performed.
  504.         int newSize = 0;
  505.         if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
  506.             newSize = (int) JdkMath.ceil(internalArray.length * expansionFactor);
  507.         } else {
  508.             newSize = (int) (internalArray.length + JdkMath.round(expansionFactor));
  509.         }
  510.         final double[] tempArray = new double[newSize];

  511.         // Copy and swap
  512.         System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
  513.         internalArray = tempArray;
  514.     }

  515.     /**
  516.      * Expands the internal storage array to the specified size.
  517.      *
  518.      * @param size Size of the new internal storage array.
  519.      */
  520.     private void expandTo(int size) {
  521.         final double[] tempArray = new double[size];
  522.         // Copy and swap
  523.         System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
  524.         internalArray = tempArray;
  525.     }

  526.     /**
  527.      * The contraction criterion defines when the internal array will contract
  528.      * to store only the number of elements in the element array.
  529.      * <p>
  530.      * If the {@code expansionMode} is {@code MULTIPLICATIVE},
  531.      * contraction is triggered when the ratio between storage array length
  532.      * and {@code numElements} exceeds {@code contractionFactor}.
  533.      * If the {@code expansionMode} is {@code ADDITIVE}, the
  534.      * number of excess storage locations is compared to {@code contractionFactor}.
  535.      *
  536.      * @return the contraction criterion used to reclaim memory.
  537.      * @since 3.1
  538.      */
  539.     public double getContractionCriterion() {
  540.         return contractionCriterion;
  541.     }

  542.     /**
  543.      * Returns the element at the specified index.
  544.      *
  545.      * @param index index to fetch a value from
  546.      * @return value stored at the specified index
  547.      * @throws ArrayIndexOutOfBoundsException if {@code index} is less than
  548.      * zero or is greater than {@code getNumElements() - 1}.
  549.      */
  550.     @Override
  551.     public double getElement(int index) {
  552.         if (index >= numElements) {
  553.             throw new ArrayIndexOutOfBoundsException(index);
  554.         } else if (index >= 0) {
  555.             return internalArray[startIndex + index];
  556.         } else {
  557.             throw new ArrayIndexOutOfBoundsException(index);
  558.         }
  559.     }

  560.      /**
  561.      * Returns a double array containing the elements of this ResizableArray.
  562.      * <p>
  563.      * This method returns a copy, not a reference to the underlying array,
  564.      * so that changes made to the returned array have no effect on this ResizableArray.
  565.      *
  566.      * @return the double array.
  567.      */
  568.     @Override
  569.     public double[] getElements() {
  570.         final double[] elementArray = new double[numElements];
  571.         System.arraycopy(internalArray, startIndex, elementArray, 0, numElements);
  572.         return elementArray;
  573.     }

  574.     /**
  575.      * The expansion factor controls the size of a new array when an array
  576.      * needs to be expanded.
  577.      * <p>
  578.      * The {@code expansionMode} determines whether the size of the array
  579.      * is multiplied by the {@code expansionFactor} (MULTIPLICATIVE) or if
  580.      * the expansion is additive (ADDITIVE -- {@code expansionFactor}
  581.      * storage locations added).  The default {@code expansionMode} is
  582.      * MULTIPLICATIVE and the default {@code expansionFactor} is 2.0.
  583.      *
  584.      * @return the expansion factor of this expandable double array
  585.      */
  586.     public double getExpansionFactor() {
  587.         return expansionFactor;
  588.     }

  589.     /**
  590.      * The expansion mode determines whether the internal storage
  591.      * array grows additively or multiplicatively when it is expanded.
  592.      *
  593.      * @return the expansion mode.
  594.      */
  595.     public ExpansionMode getExpansionMode() {
  596.         return expansionMode;
  597.     }

  598.     /**
  599.      * Gets the currently allocated size of the internal data structure used
  600.      * for storing elements.
  601.      * This is not to be confused with {@link #getNumElements() the number of
  602.      * elements actually stored}.
  603.      *
  604.      * @return the length of the internal array.
  605.      * @since 3.1
  606.      */
  607.     public int getCapacity() {
  608.         return internalArray.length;
  609.     }

  610.     /**
  611.      * Returns the number of elements currently in the array.  Please note
  612.      * that this is different from the length of the internal storage array.
  613.      *
  614.      * @return the number of elements.
  615.      */
  616.     @Override
  617.     public int getNumElements() {
  618.         return numElements;
  619.     }

  620.     /**
  621.      * Provides <em>direct</em> access to the internal storage array.
  622.      * Please note that this method returns a reference to this object's
  623.      * storage array, not a copy.
  624.      * <p>
  625.      * To correctly address elements of the array, the "start index" is
  626.      * required (available via the {@link #getStartIndex() getStartIndex}
  627.      * method.
  628.      * <p>
  629.      * This method should only be used to avoid copying the internal array.
  630.      * The returned value <em>must</em> be used for reading only; other
  631.      * uses could lead to this object becoming inconsistent.
  632.      * <p>
  633.      * The {@link #getElements} method has no such limitation since it
  634.      * returns a copy of this array's addressable elements.
  635.      *
  636.      * @return the internal storage array used by this object.
  637.      * @since 3.1
  638.      */
  639.     protected double[] getArrayRef() {
  640.         return internalArray;
  641.     }

  642.     /**
  643.      * Returns the "start index" of the internal array.
  644.      * This index is the position of the first addressable element in the
  645.      * internal storage array.
  646.      * <p>
  647.      * The addressable elements in the array are at indices contained in
  648.      * the interval [{@link #getStartIndex()},
  649.      *               {@link #getStartIndex()} + {@link #getNumElements()} - 1].
  650.      *
  651.      * @return the start index.
  652.      * @since 3.1
  653.      */
  654.     protected int getStartIndex() {
  655.         return startIndex;
  656.     }

  657.     /**
  658.      * Performs an operation on the addressable elements of the array.
  659.      *
  660.      * @param f Function to be applied on this array.
  661.      * @return the result.
  662.      * @since 3.1
  663.      */
  664.     public double compute(MathArrays.Function f) {
  665.         return f.evaluate(internalArray, startIndex, numElements);
  666.     }

  667.     /**
  668.      * Sets the element at the specified index.
  669.      * <p>
  670.      * If the specified index is greater than {@code getNumElements() - 1},
  671.      * the {@code numElements} property is increased to {@code index +1}
  672.      * and additional storage is allocated (if necessary) for the new element and
  673.      * all (uninitialized) elements between the new element and the previous end
  674.      * of the array).
  675.      *
  676.      * @param index index to store a value in
  677.      * @param value value to store at the specified index
  678.      * @throws ArrayIndexOutOfBoundsException if {@code index < 0}.
  679.      */
  680.     @Override
  681.     public void setElement(int index, double value) {
  682.         if (index < 0) {
  683.             throw new ArrayIndexOutOfBoundsException(index);
  684.         }
  685.         if (index + 1 > numElements) {
  686.             numElements = index + 1;
  687.         }
  688.         if ((startIndex + index) >= internalArray.length) {
  689.             expandTo(startIndex + (index + 1));
  690.         }
  691.         internalArray[startIndex + index] = value;
  692.     }

  693.     /**
  694.      * This function allows you to control the number of elements contained
  695.      * in this array, and can be used to "throw out" the last n values in an
  696.      * array. This function will also expand the internal array as needed.
  697.      *
  698.      * @param i a new number of elements
  699.      * @throws MathIllegalArgumentException if {@code i} is negative.
  700.      */
  701.     public void setNumElements(int i) throws MathIllegalArgumentException {
  702.         // If index is negative thrown an error.
  703.         if (i < 0) {
  704.             throw new MathIllegalArgumentException(LocalizedFormats.INDEX_NOT_POSITIVE, i);
  705.         }

  706.         // Test the new num elements, check to see if the array needs to be
  707.         // expanded to accommodate this new number of elements.
  708.         final int newSize = startIndex + i;
  709.         if (newSize > internalArray.length) {
  710.             expandTo(newSize);
  711.         }

  712.         // Set the new number of elements to new value.
  713.         numElements = i;
  714.     }

  715.     /**
  716.      * Returns true if the internal storage array has too many unused
  717.      * storage positions.
  718.      *
  719.      * @return true if array satisfies the contraction criteria
  720.      */
  721.     private boolean shouldContract() {
  722.         if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
  723.             return (internalArray.length / ((float) numElements)) > contractionCriterion;
  724.         } else {
  725.             return (internalArray.length - numElements) > contractionCriterion;
  726.         }
  727.     }

  728.     /**
  729.      * Returns a copy of the ResizableDoubleArray.  Does not contract before
  730.      * the copy, so the returned object is an exact copy of this.
  731.      *
  732.      * @return a new ResizableDoubleArray with the same data and configuration
  733.      * properties as this
  734.      * @since 2.0
  735.      */
  736.     public ResizableDoubleArray copy() {
  737.         return new ResizableDoubleArray(this);
  738.     }

  739.     /**
  740.      * Returns true iff object is a ResizableDoubleArray with the same properties
  741.      * as this and an identical internal storage array.
  742.      *
  743.      * @param object object to be compared for equality with this
  744.      * @return true iff object is a ResizableDoubleArray with the same data and
  745.      * properties as this
  746.      * @since 2.0
  747.      */
  748.     @Override
  749.     public boolean equals(Object object) {
  750.         if (object == this ) {
  751.             return true;
  752.         }
  753.         if (!(object instanceof ResizableDoubleArray)) {
  754.             return false;
  755.         }
  756.         boolean result = true;
  757.         final ResizableDoubleArray other = (ResizableDoubleArray) object;
  758.         result = result && other.contractionCriterion == contractionCriterion;
  759.         result = result && other.expansionFactor == expansionFactor;
  760.         result = result && other.expansionMode == expansionMode;
  761.         result = result && other.numElements == numElements;
  762.         result = result && other.startIndex == startIndex;
  763.         if (!result) {
  764.             return false;
  765.         } else {
  766.             return Arrays.equals(internalArray, other.internalArray);
  767.         }
  768.     }

  769.     /**
  770.      * Returns a hash code consistent with equals.
  771.      *
  772.      * @return the hash code representing this {@code ResizableDoubleArray}.
  773.      * @since 2.0
  774.      */
  775.     @Override
  776.     public int hashCode() {
  777.         final int[] hashData = new int[6];
  778.         hashData[0] = Double.valueOf(expansionFactor).hashCode();
  779.         hashData[1] = Double.valueOf(contractionCriterion).hashCode();
  780.         hashData[2] = expansionMode.hashCode();
  781.         hashData[3] = Arrays.hashCode(internalArray);
  782.         hashData[4] = numElements;
  783.         hashData[5] = startIndex;
  784.         return Arrays.hashCode(hashData);
  785.     }
  786. }