001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math3.util;
018
019import java.io.Serializable;
020import java.util.Arrays;
021
022import org.apache.commons.math3.exception.MathIllegalArgumentException;
023import org.apache.commons.math3.exception.MathIllegalStateException;
024import org.apache.commons.math3.exception.MathInternalError;
025import org.apache.commons.math3.exception.NullArgumentException;
026import org.apache.commons.math3.exception.NotStrictlyPositiveException;
027import org.apache.commons.math3.exception.NumberIsTooSmallException;
028import org.apache.commons.math3.exception.util.LocalizedFormats;
029
030/**
031 * <p>
032 * A variable length {@link DoubleArray} implementation that automatically
033 * handles expanding and contracting its internal storage array as elements
034 * are added and removed.
035 * </p>
036 * <h3>Important note: Usage should not assume that this class is thread-safe
037 * even though some of the methods are {@code synchronized}.
038 * This qualifier will be dropped in the next major release (4.0).</h3>
039 * <p>
040 * The internal storage array starts with capacity determined by the
041 * {@code initialCapacity} property, which can be set by the constructor.
042 * The default initial capacity is 16.  Adding elements using
043 * {@link #addElement(double)} appends elements to the end of the array.
044 * When there are no open entries at the end of the internal storage array,
045 * the array is expanded.  The size of the expanded array depends on the
046 * {@code expansionMode} and {@code expansionFactor} properties.
047 * The {@code expansionMode} determines whether the size of the array is
048 * multiplied by the {@code expansionFactor}
049 * ({@link ExpansionMode#MULTIPLICATIVE}) or if the expansion is additive
050 * ({@link ExpansionMode#ADDITIVE} -- {@code expansionFactor} storage
051 * locations added).
052 * The default {@code expansionMode} is {@code MULTIPLICATIVE} and the default
053 * {@code expansionFactor} is 2.
054 * </p>
055 * <p>
056 * The {@link #addElementRolling(double)} method adds a new element to the end
057 * of the internal storage array and adjusts the "usable window" of the
058 * internal array forward by one position (effectively making what was the
059 * second element the first, and so on).  Repeated activations of this method
060 * (or activation of {@link #discardFrontElements(int)}) will effectively orphan
061 * the storage locations at the beginning of the internal storage array.  To
062 * reclaim this storage, each time one of these methods is activated, the size
063 * of the internal storage array is compared to the number of addressable
064 * elements (the {@code numElements} property) and if the difference
065 * is too large, the internal array is contracted to size
066 * {@code numElements + 1}.  The determination of when the internal
067 * storage array is "too large" depends on the {@code expansionMode} and
068 * {@code contractionFactor} properties.  If  the {@code expansionMode}
069 * is {@code MULTIPLICATIVE}, contraction is triggered when the
070 * ratio between storage array length and {@code numElements} exceeds
071 * {@code contractionFactor.}  If the {@code expansionMode}
072 * is {@code ADDITIVE}, the number of excess storage locations
073 * is compared to {@code contractionFactor}.
074 * </p>
075 * <p>
076 * To avoid cycles of expansions and contractions, the
077 * {@code expansionFactor} must not exceed the {@code contractionFactor}.
078 * Constructors and mutators for both of these properties enforce this
079 * requirement, throwing a {@code MathIllegalArgumentException} if it is
080 * violated.
081 * </p>
082 */
083public class ResizableDoubleArray implements DoubleArray, Serializable {
084    /** Additive expansion mode.
085     * @deprecated As of 3.1. Please use {@link ExpansionMode#ADDITIVE} instead.
086     */
087    @Deprecated
088    public static final int ADDITIVE_MODE = 1;
089    /** Multiplicative expansion mode.
090     * @deprecated As of 3.1. Please use {@link ExpansionMode#MULTIPLICATIVE} instead.
091     */
092    @Deprecated
093    public static final int MULTIPLICATIVE_MODE = 0;
094    /** Serializable version identifier. */
095    private static final long serialVersionUID = -3485529955529426875L;
096
097    /** Default value for initial capacity. */
098    private static final int DEFAULT_INITIAL_CAPACITY = 16;
099    /** Default value for array size modifier. */
100    private static final double DEFAULT_EXPANSION_FACTOR = 2.0;
101    /**
102     * Default value for the difference between {@link #contractionCriterion}
103     * and {@link #expansionFactor}.
104     */
105    private static final double DEFAULT_CONTRACTION_DELTA = 0.5;
106
107    /**
108     * The contraction criteria determines when the internal array will be
109     * contracted to fit the number of elements contained in the element
110     *  array + 1.
111     */
112    private double contractionCriterion = 2.5;
113
114    /**
115     * The expansion factor of the array.  When the array needs to be expanded,
116     * the new array size will be
117     * {@code internalArray.length * expansionFactor}
118     * if {@code expansionMode} is set to MULTIPLICATIVE_MODE, or
119     * {@code internalArray.length + expansionFactor} if
120     * {@code expansionMode} is set to ADDITIVE_MODE.
121     */
122    private double expansionFactor = 2.0;
123
124    /**
125     * Determines whether array expansion by {@code expansionFactor}
126     * is additive or multiplicative.
127     */
128    private ExpansionMode expansionMode = ExpansionMode.MULTIPLICATIVE;
129
130    /**
131     * The internal storage array.
132     */
133    private double[] internalArray;
134
135    /**
136     * The number of addressable elements in the array.  Note that this
137     * has nothing to do with the length of the internal storage array.
138     */
139    private int numElements = 0;
140
141    /**
142     * The position of the first addressable element in the internal storage
143     * array.  The addressable elements in the array are
144     * {@code internalArray[startIndex],...,internalArray[startIndex + numElements - 1]}.
145     */
146    private int startIndex = 0;
147
148    /**
149     * Specification of expansion algorithm.
150     * @since 3.1
151     */
152    public enum ExpansionMode {
153        /** Multiplicative expansion mode. */
154        MULTIPLICATIVE,
155        /** Additive expansion mode. */
156        ADDITIVE
157    }
158
159    /**
160     * Creates an instance with default properties.
161     * <ul>
162     *  <li>{@code initialCapacity = 16}</li>
163     *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
164     *  <li>{@code expansionFactor = 2.0}</li>
165     *  <li>{@code contractionCriterion = 2.5}</li>
166     * </ul>
167     */
168    public ResizableDoubleArray() {
169        this(DEFAULT_INITIAL_CAPACITY);
170    }
171
172    /**
173     * Creates an instance with the specified initial capacity.
174     * Other properties take default values:
175     * <ul>
176     *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
177     *  <li>{@code expansionFactor = 2.0}</li>
178     *  <li>{@code contractionCriterion = 2.5}</li>
179     * </ul>
180     * @param initialCapacity Initial size of the internal storage array.
181     * @throws MathIllegalArgumentException if {@code initialCapacity <= 0}.
182     */
183    public ResizableDoubleArray(int initialCapacity)
184        throws MathIllegalArgumentException {
185        this(initialCapacity, DEFAULT_EXPANSION_FACTOR);
186    }
187
188    /**
189     * Creates an instance from an existing {@code double[]} with the
190     * initial capacity and numElements corresponding to the size of
191     * the supplied {@code double[]} array.
192     * If the supplied array is null, a new empty array with the default
193     * initial capacity will be created.
194     * The input array is copied, not referenced.
195     * Other properties take default values:
196     * <ul>
197     *  <li>{@code initialCapacity = 16}</li>
198     *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
199     *  <li>{@code expansionFactor = 2.0}</li>
200     *  <li>{@code contractionCriterion = 2.5}</li>
201     * </ul>
202     *
203     * @param initialArray initial array
204     * @since 2.2
205     */
206    public ResizableDoubleArray(double[] initialArray) {
207        this(DEFAULT_INITIAL_CAPACITY,
208             DEFAULT_EXPANSION_FACTOR,
209             DEFAULT_CONTRACTION_DELTA + DEFAULT_EXPANSION_FACTOR,
210             ExpansionMode.MULTIPLICATIVE,
211             initialArray);
212    }
213
214    /**
215     * Creates an instance with the specified initial capacity
216     * and expansion factor.
217     * The remaining properties take default values:
218     * <ul>
219     *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
220     *  <li>{@code contractionCriterion = 0.5 + expansionFactor}</li>
221     * </ul>
222     * <br/>
223     * Throws IllegalArgumentException if the following conditions are
224     * not met:
225     * <ul>
226     *  <li>{@code initialCapacity > 0}</li>
227     *  <li>{@code expansionFactor > 1}</li>
228     * </ul>
229     *
230     * @param initialCapacity Initial size of the internal storage array.
231     * @param expansionFactor The array will be expanded based on this
232     * parameter.
233     * @throws MathIllegalArgumentException if parameters are not valid.
234     * @deprecated As of 3.1. Please use
235     * {@link #ResizableDoubleArray(int,double)} instead.
236     */
237    @Deprecated
238    public ResizableDoubleArray(int initialCapacity,
239                                float expansionFactor)
240        throws MathIllegalArgumentException {
241        this(initialCapacity,
242             (double) expansionFactor);
243    }
244
245    /**
246     * Creates an instance with the specified initial capacity
247     * and expansion factor.
248     * The remaining properties take default values:
249     * <ul>
250     *  <li>{@code expansionMode = MULTIPLICATIVE}</li>
251     *  <li>{@code contractionCriterion = 0.5 + expansionFactor}</li>
252     * </ul>
253     * <br/>
254     * Throws IllegalArgumentException if the following conditions are
255     * not met:
256     * <ul>
257     *  <li>{@code initialCapacity > 0}</li>
258     *  <li>{@code expansionFactor > 1}</li>
259     * </ul>
260     *
261     * @param initialCapacity Initial size of the internal storage array.
262     * @param expansionFactor The array will be expanded based on this
263     * parameter.
264     * @throws MathIllegalArgumentException if parameters are not valid.
265     * @since 3.1
266     */
267    public ResizableDoubleArray(int initialCapacity,
268                                double expansionFactor)
269        throws MathIllegalArgumentException {
270        this(initialCapacity,
271             expansionFactor,
272             DEFAULT_CONTRACTION_DELTA + expansionFactor);
273    }
274
275    /**
276     * Creates an instance with the specified initialCapacity,
277     * expansionFactor, and contractionCriterion.
278     * The expansion mode will default to {@code MULTIPLICATIVE}.
279     * <br/>
280     * Throws IllegalArgumentException if the following conditions are
281     * not met:
282     * <ul>
283     *  <li>{@code initialCapacity > 0}</li>
284     *  <li>{@code expansionFactor > 1}</li>
285     *  <li>{@code contractionCriterion >= expansionFactor}</li>
286     * </ul>
287     *
288     * @param initialCapacity Initial size of the internal storage array..
289     * @param expansionFactor The array will be expanded based on this
290     * parameter.
291     * @param contractionCriteria Contraction criteria.
292     * @throws MathIllegalArgumentException if parameters are not valid.
293     * @deprecated As of 3.1. Please use
294     * {@link #ResizableDoubleArray(int,double,double)} instead.
295     */
296    @Deprecated
297    public ResizableDoubleArray(int initialCapacity,
298                                float expansionFactor,
299                                float contractionCriteria)
300        throws MathIllegalArgumentException {
301        this(initialCapacity,
302             (double) expansionFactor,
303             (double) contractionCriteria);
304    }
305
306    /**
307     * Creates an instance with the specified initial capacity,
308     * expansion factor, and contraction criteria.
309     * The expansion mode will default to {@code MULTIPLICATIVE}.
310     * <br/>
311     * Throws IllegalArgumentException if the following conditions are
312     * not met:
313     * <ul>
314     *  <li>{@code initialCapacity > 0}</li>
315     *  <li>{@code expansionFactor > 1}</li>
316     *  <li>{@code contractionCriterion >= expansionFactor}</li>
317     * </ul>
318     *
319     * @param initialCapacity Initial size of the internal storage array..
320     * @param expansionFactor The array will be expanded based on this
321     * parameter.
322     * @param contractionCriterion Contraction criterion.
323     * @throws MathIllegalArgumentException if the parameters are not valid.
324     * @since 3.1
325     */
326    public ResizableDoubleArray(int initialCapacity,
327                                double expansionFactor,
328                                double contractionCriterion)
329        throws MathIllegalArgumentException {
330        this(initialCapacity,
331             expansionFactor,
332             contractionCriterion,
333             ExpansionMode.MULTIPLICATIVE,
334             null);
335    }
336
337    /**
338     * <p>
339     * Create a ResizableArray with the specified properties.</p>
340     * <p>
341     * Throws IllegalArgumentException if the following conditions are
342     * not met:
343     * <ul>
344     * <li><code>initialCapacity > 0</code></li>
345     * <li><code>expansionFactor > 1</code></li>
346     * <li><code>contractionFactor >= expansionFactor</code></li>
347     * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code>
348     * </li>
349     * </ul></p>
350     *
351     * @param initialCapacity the initial size of the internal storage array
352     * @param expansionFactor the array will be expanded based on this
353     *                        parameter
354     * @param contractionCriteria the contraction Criteria
355     * @param expansionMode  the expansion mode
356     * @throws MathIllegalArgumentException if parameters are not valid
357     * @deprecated As of 3.1. Please use
358     * {@link #ResizableDoubleArray(int,double,double,ExpansionMode,double[])}
359     * instead.
360     */
361    @Deprecated
362    public ResizableDoubleArray(int initialCapacity, float expansionFactor,
363            float contractionCriteria, int expansionMode) throws MathIllegalArgumentException {
364        this(initialCapacity,
365             expansionFactor,
366             contractionCriteria,
367             expansionMode == ADDITIVE_MODE ?
368             ExpansionMode.ADDITIVE :
369             ExpansionMode.MULTIPLICATIVE,
370             null);
371        // XXX Just ot retain the expected failure in a unit test.
372        // With the new "enum", that test will become obsolete.
373        setExpansionMode(expansionMode);
374    }
375
376    /**
377     * Creates an instance with the specified properties.
378     * <br/>
379     * Throws MathIllegalArgumentException if the following conditions are
380     * not met:
381     * <ul>
382     *  <li>{@code initialCapacity > 0}</li>
383     *  <li>{@code expansionFactor > 1}</li>
384     *  <li>{@code contractionCriterion >= expansionFactor}</li>
385     * </ul>
386     *
387     * @param initialCapacity Initial size of the internal storage array.
388     * @param expansionFactor The array will be expanded based on this
389     * parameter.
390     * @param contractionCriterion Contraction criteria.
391     * @param expansionMode Expansion mode.
392     * @param data Initial contents of the array.
393     * @throws MathIllegalArgumentException if the parameters are not valid.
394     */
395    public ResizableDoubleArray(int initialCapacity,
396                                double expansionFactor,
397                                double contractionCriterion,
398                                ExpansionMode expansionMode,
399                                double ... data)
400        throws MathIllegalArgumentException {
401        if (initialCapacity <= 0) {
402            throw new NotStrictlyPositiveException(LocalizedFormats.INITIAL_CAPACITY_NOT_POSITIVE,
403                                                   initialCapacity);
404        }
405        checkContractExpand(contractionCriterion, expansionFactor);
406
407        this.expansionFactor = expansionFactor;
408        this.contractionCriterion = contractionCriterion;
409        this.expansionMode = expansionMode;
410        internalArray = new double[initialCapacity];
411        numElements = 0;
412        startIndex = 0;
413
414        if (data != null && data.length > 0) {
415            addElements(data);
416        }
417    }
418
419    /**
420     * Copy constructor.  Creates a new ResizableDoubleArray that is a deep,
421     * fresh copy of the original. Needs to acquire synchronization lock
422     * on original.  Original may not be null; otherwise a {@link NullArgumentException}
423     * is thrown.
424     *
425     * @param original array to copy
426     * @exception NullArgumentException if original is null
427     * @since 2.0
428     */
429    public ResizableDoubleArray(ResizableDoubleArray original)
430        throws NullArgumentException {
431        MathUtils.checkNotNull(original);
432        copy(original, this);
433    }
434
435    /**
436     * Adds an element to the end of this expandable array.
437     *
438     * @param value Value to be added to end of array.
439     */
440    public synchronized void addElement(double value) {
441        if (internalArray.length <= startIndex + numElements) {
442            expand();
443        }
444        internalArray[startIndex + numElements++] = value;
445    }
446
447    /**
448     * Adds several element to the end of this expandable array.
449     *
450     * @param values Values to be added to end of array.
451     * @since 2.2
452     */
453    public synchronized void addElements(double[] values) {
454        final double[] tempArray = new double[numElements + values.length + 1];
455        System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
456        System.arraycopy(values, 0, tempArray, numElements, values.length);
457        internalArray = tempArray;
458        startIndex = 0;
459        numElements += values.length;
460    }
461
462    /**
463     * <p>
464     * Adds an element to the end of the array and removes the first
465     * element in the array.  Returns the discarded first element.
466     * The effect is similar to a push operation in a FIFO queue.
467     * </p>
468     * <p>
469     * Example: If the array contains the elements 1, 2, 3, 4 (in that order)
470     * and addElementRolling(5) is invoked, the result is an array containing
471     * the entries 2, 3, 4, 5 and the value returned is 1.
472     * </p>
473     *
474     * @param value Value to be added to the array.
475     * @return the value which has been discarded or "pushed" out of the array
476     * by this rolling insert.
477     */
478    public synchronized double addElementRolling(double value) {
479        double discarded = internalArray[startIndex];
480
481        if ((startIndex + (numElements + 1)) > internalArray.length) {
482            expand();
483        }
484        // Increment the start index
485        startIndex += 1;
486
487        // Add the new value
488        internalArray[startIndex + (numElements - 1)] = value;
489
490        // Check the contraction criterion.
491        if (shouldContract()) {
492            contract();
493        }
494        return discarded;
495    }
496
497    /**
498     * Substitutes <code>value</code> for the most recently added value.
499     * Returns the value that has been replaced. If the array is empty (i.e.
500     * if {@link #numElements} is zero), an IllegalStateException is thrown.
501     *
502     * @param value New value to substitute for the most recently added value
503     * @return the value that has been replaced in the array.
504     * @throws MathIllegalStateException if the array is empty
505     * @since 2.0
506     */
507    public synchronized double substituteMostRecentElement(double value)
508        throws MathIllegalStateException {
509        if (numElements < 1) {
510            throw new MathIllegalStateException(
511                    LocalizedFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY);
512        }
513
514        final int substIndex = startIndex + (numElements - 1);
515        final double discarded = internalArray[substIndex];
516
517        internalArray[substIndex] = value;
518
519        return discarded;
520    }
521
522    /**
523     * Checks the expansion factor and the contraction criterion and throws an
524     * IllegalArgumentException if the contractionCriteria is less than the
525     * expansionCriteria
526     *
527     * @param expansion factor to be checked
528     * @param contraction criteria to be checked
529     * @throws MathIllegalArgumentException if the contractionCriteria is less than
530     * the expansionCriteria.
531     * @deprecated As of 3.1. Please use
532     * {@link #checkContractExpand(double,double)} instead.
533     */
534    @Deprecated
535    protected void checkContractExpand(float contraction, float expansion)
536        throws MathIllegalArgumentException {
537        checkContractExpand((double) contraction,
538                            (double) expansion);
539    }
540
541    /**
542     * Checks the expansion factor and the contraction criterion and raises
543     * an exception if the contraction criterion is smaller than the
544     * expansion criterion.
545     *
546     * @param contraction Criterion to be checked.
547     * @param expansion Factor to be checked.
548     * @throws NumberIsTooSmallException if {@code contraction < expansion}.
549     * @throws NumberIsTooSmallException if {@code contraction <= 1}.
550     * @throws NumberIsTooSmallException if {@code expansion <= 1 }.
551     * @since 3.1
552     */
553    protected void checkContractExpand(double contraction,
554                                       double expansion)
555        throws NumberIsTooSmallException {
556        if (contraction < expansion) {
557            final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, true);
558            e.getContext().addMessage(LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR,
559                                      contraction, expansion);
560            throw e;
561        }
562
563        if (contraction <= 1) {
564            final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, false);
565            e.getContext().addMessage(LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE,
566                                      contraction);
567            throw e;
568        }
569
570        if (expansion <= 1) {
571            final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, false);
572            e.getContext().addMessage(LocalizedFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE,
573                                      expansion);
574            throw e;
575        }
576    }
577
578    /**
579     * Clear the array contents, resetting the number of elements to zero.
580     */
581    public synchronized void clear() {
582        numElements = 0;
583        startIndex = 0;
584    }
585
586    /**
587     * Contracts the storage array to the (size of the element set) + 1 - to
588     * avoid a zero length array. This function also resets the startIndex to
589     * zero.
590     */
591    public synchronized void contract() {
592        final double[] tempArray = new double[numElements + 1];
593
594        // Copy and swap - copy only the element array from the src array.
595        System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
596        internalArray = tempArray;
597
598        // Reset the start index to zero
599        startIndex = 0;
600    }
601
602    /**
603     * Discards the <code>i</code> initial elements of the array.  For example,
604     * if the array contains the elements 1,2,3,4, invoking
605     * <code>discardFrontElements(2)</code> will cause the first two elements
606     * to be discarded, leaving 3,4 in the array.  Throws illegalArgumentException
607     * if i exceeds numElements.
608     *
609     * @param i  the number of elements to discard from the front of the array
610     * @throws MathIllegalArgumentException if i is greater than numElements.
611     * @since 2.0
612     */
613    public synchronized void discardFrontElements(int i)
614        throws MathIllegalArgumentException {
615        discardExtremeElements(i,true);
616    }
617
618    /**
619     * Discards the <code>i</code> last elements of the array.  For example,
620     * if the array contains the elements 1,2,3,4, invoking
621     * <code>discardMostRecentElements(2)</code> will cause the last two elements
622     * to be discarded, leaving 1,2 in the array.  Throws illegalArgumentException
623     * if i exceeds numElements.
624     *
625     * @param i  the number of elements to discard from the end of the array
626     * @throws MathIllegalArgumentException if i is greater than numElements.
627     * @since 2.0
628     */
629    public synchronized void discardMostRecentElements(int i)
630        throws MathIllegalArgumentException {
631        discardExtremeElements(i,false);
632    }
633
634    /**
635     * Discards the <code>i</code> first or last elements of the array,
636     * depending on the value of <code>front</code>.
637     * For example, if the array contains the elements 1,2,3,4, invoking
638     * <code>discardExtremeElements(2,false)</code> will cause the last two elements
639     * to be discarded, leaving 1,2 in the array.
640     * For example, if the array contains the elements 1,2,3,4, invoking
641     * <code>discardExtremeElements(2,true)</code> will cause the first two elements
642     * to be discarded, leaving 3,4 in the array.
643     * Throws illegalArgumentException
644     * if i exceeds numElements.
645     *
646     * @param i  the number of elements to discard from the front/end of the array
647     * @param front true if elements are to be discarded from the front
648     * of the array, false if elements are to be discarded from the end
649     * of the array
650     * @throws MathIllegalArgumentException if i is greater than numElements.
651     * @since 2.0
652     */
653    private synchronized void discardExtremeElements(int i,
654                                                     boolean front)
655        throws MathIllegalArgumentException {
656        if (i > numElements) {
657            throw new MathIllegalArgumentException(
658                    LocalizedFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY,
659                    i, numElements);
660       } else if (i < 0) {
661           throw new MathIllegalArgumentException(
662                   LocalizedFormats.CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS,
663                   i);
664        } else {
665            // "Subtract" this number of discarded from numElements
666            numElements -= i;
667            if (front) {
668                startIndex += i;
669            }
670        }
671        if (shouldContract()) {
672            contract();
673        }
674    }
675
676    /**
677     * Expands the internal storage array using the expansion factor.
678     * <p>
679     * if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE,
680     * the new array size will be <code>internalArray.length * expansionFactor.</code>
681     * If <code>expansionMode</code> is set to ADDITIVE_MODE,  the length
682     * after expansion will be <code>internalArray.length + expansionFactor</code>
683     * </p>
684     */
685    protected synchronized void expand() {
686        // notice the use of FastMath.ceil(), this guarantees that we will always
687        // have an array of at least currentSize + 1.   Assume that the
688        // current initial capacity is 1 and the expansion factor
689        // is 1.000000000000000001.  The newly calculated size will be
690        // rounded up to 2 after the multiplication is performed.
691        int newSize = 0;
692        if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
693            newSize = (int) FastMath.ceil(internalArray.length * expansionFactor);
694        } else {
695            newSize = (int) (internalArray.length + FastMath.round(expansionFactor));
696        }
697        final double[] tempArray = new double[newSize];
698
699        // Copy and swap
700        System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
701        internalArray = tempArray;
702    }
703
704    /**
705     * Expands the internal storage array to the specified size.
706     *
707     * @param size Size of the new internal storage array.
708     */
709    private synchronized void expandTo(int size) {
710        final double[] tempArray = new double[size];
711        // Copy and swap
712        System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
713        internalArray = tempArray;
714    }
715
716    /**
717     * The contraction criteria defines when the internal array will contract
718     * to store only the number of elements in the element array.
719     * If  the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>,
720     * contraction is triggered when the ratio between storage array length
721     * and <code>numElements</code> exceeds <code>contractionFactor</code>.
722     * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the
723     * number of excess storage locations is compared to
724     * <code>contractionFactor.</code>
725     *
726     * @return the contraction criteria used to reclaim memory.
727     * @deprecated As of 3.1. Please use {@link #getContractionCriterion()}
728     * instead.
729     */
730    @Deprecated
731    public float getContractionCriteria() {
732        return (float) getContractionCriterion();
733    }
734
735    /**
736     * The contraction criterion defines when the internal array will contract
737     * to store only the number of elements in the element array.
738     * If  the <code>expansionMode</code> is <code>MULTIPLICATIVE_MODE</code>,
739     * contraction is triggered when the ratio between storage array length
740     * and <code>numElements</code> exceeds <code>contractionFactor</code>.
741     * If the <code>expansionMode</code> is <code>ADDITIVE_MODE</code>, the
742     * number of excess storage locations is compared to
743     * <code>contractionFactor.</code>
744     *
745     * @return the contraction criterion used to reclaim memory.
746     * @since 3.1
747     */
748    public double getContractionCriterion() {
749        return contractionCriterion;
750    }
751
752    /**
753     * Returns the element at the specified index
754     *
755     * @param index index to fetch a value from
756     * @return value stored at the specified index
757     * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than
758     * zero or is greater than <code>getNumElements() - 1</code>.
759     */
760    public synchronized double getElement(int index) {
761        if (index >= numElements) {
762            throw new ArrayIndexOutOfBoundsException(index);
763        } else if (index >= 0) {
764            return internalArray[startIndex + index];
765        } else {
766            throw new ArrayIndexOutOfBoundsException(index);
767        }
768    }
769
770     /**
771     * Returns a double array containing the elements of this
772     * <code>ResizableArray</code>.  This method returns a copy, not a
773     * reference to the underlying array, so that changes made to the returned
774     *  array have no effect on this <code>ResizableArray.</code>
775     * @return the double array.
776     */
777    public synchronized double[] getElements() {
778        final double[] elementArray = new double[numElements];
779        System.arraycopy(internalArray, startIndex, elementArray, 0, numElements);
780        return elementArray;
781    }
782
783    /**
784     * The expansion factor controls the size of a new array when an array
785     * needs to be expanded.  The <code>expansionMode</code>
786     * determines whether the size of the array is multiplied by the
787     * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if
788     * the expansion is additive (ADDITIVE_MODE -- <code>expansionFactor</code>
789     * storage locations added).  The default <code>expansionMode</code> is
790     * MULTIPLICATIVE_MODE and the default <code>expansionFactor</code>
791     * is 2.0.
792     *
793     * @return the expansion factor of this expandable double array
794     * @deprecated As of 3.1. Return type will be changed to "double" in 4.0.
795     */
796    @Deprecated
797    public float getExpansionFactor() {
798        return (float) expansionFactor;
799    }
800
801    /**
802     * The expansion mode determines whether the internal storage
803     * array grows additively or multiplicatively when it is expanded.
804     *
805     * @return the expansion mode.
806     * @deprecated As of 3.1. Return value to be changed to
807     * {@link ExpansionMode} in 4.0.
808     */
809    @Deprecated
810    public int getExpansionMode() {
811        synchronized (this) {
812            switch (expansionMode) {
813                case MULTIPLICATIVE:
814                    return MULTIPLICATIVE_MODE;
815                case ADDITIVE:
816                    return ADDITIVE_MODE;
817                default:
818                    throw new MathInternalError(); // Should never happen.
819            }
820        }
821    }
822
823    /**
824     * Notice the package scope on this method.   This method is simply here
825     * for the JUnit test, it allows us check if the expansion is working
826     * properly after a number of expansions.  This is not meant to be a part
827     * of the public interface of this class.
828     *
829     * @return the length of the internal storage array.
830     * @deprecated As of 3.1. Please use {@link #getCapacity()} instead.
831     */
832    @Deprecated
833    synchronized int getInternalLength() {
834        return internalArray.length;
835    }
836
837    /**
838     * Gets the currently allocated size of the internal data structure used
839     * for storing elements.
840     * This is not to be confused with {@link #getNumElements() the number of
841     * elements actually stored}.
842     *
843     * @return the length of the internal array.
844     * @since 3.1
845     */
846    public int getCapacity() {
847        return internalArray.length;
848    }
849
850    /**
851     * Returns the number of elements currently in the array.  Please note
852     * that this is different from the length of the internal storage array.
853     *
854     * @return the number of elements.
855     */
856    public synchronized int getNumElements() {
857        return numElements;
858    }
859
860    /**
861     * Returns the internal storage array.  Note that this method returns
862     * a reference to the internal storage array, not a copy, and to correctly
863     * address elements of the array, the <code>startIndex</code> is
864     * required (available via the {@link #start} method).  This method should
865     * only be used in cases where copying the internal array is not practical.
866     * The {@link #getElements} method should be used in all other cases.
867     *
868     *
869     * @return the internal storage array used by this object
870     * @since 2.0
871     * @deprecated As of 3.1.
872     */
873    @Deprecated
874    public synchronized double[] getInternalValues() {
875        return internalArray;
876    }
877
878    /**
879     * Provides <em>direct</em> access to the internal storage array.
880     * Please note that this method returns a reference to this object's
881     * storage array, not a copy.
882     * <br/>
883     * To correctly address elements of the array, the "start index" is
884     * required (available via the {@link #getStartIndex() getStartIndex}
885     * method.
886     * <br/>
887     * This method should only be used to avoid copying the internal array.
888     * The returned value <em>must</em> be used for reading only; other
889     * uses could lead to this object becoming inconsistent.
890     * <br/>
891     * The {@link #getElements} method has no such limitation since it
892     * returns a copy of this array's addressable elements.
893     *
894     * @return the internal storage array used by this object.
895     * @since 3.1
896     */
897    protected double[] getArrayRef() {
898        return internalArray;
899    }
900
901    /**
902     * Returns the "start index" of the internal array.
903     * This index is the position of the first addressable element in the
904     * internal storage array.
905     * The addressable elements in the array are at indices contained in
906     * the interval [{@link #getStartIndex()},
907     *               {@link #getStartIndex()} + {@link #getNumElements()} - 1].
908     *
909     * @return the start index.
910     * @since 3.1
911     */
912    protected int getStartIndex() {
913        return startIndex;
914    }
915
916    /**
917     * Sets the contraction criteria.
918     *
919     * @param contractionCriteria contraction criteria
920     * @throws MathIllegalArgumentException if the contractionCriteria is less than
921     *         the expansionCriteria.
922     * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
923     */
924    @Deprecated
925    public void setContractionCriteria(float contractionCriteria)
926        throws MathIllegalArgumentException {
927        checkContractExpand(contractionCriteria, getExpansionFactor());
928        synchronized(this) {
929            this.contractionCriterion = contractionCriteria;
930        }
931    }
932
933    /**
934     * Performs an operation on the addressable elements of the array.
935     *
936     * @param f Function to be applied on this array.
937     * @return the result.
938     * @since 3.1
939     */
940    public double compute(MathArrays.Function f) {
941        final double[] array;
942        final int start;
943        final int num;
944        synchronized(this) {
945            array = internalArray;
946            start = startIndex;
947            num   = numElements;
948        }
949        return f.evaluate(array, start, num);
950    }
951
952    /**
953     * Sets the element at the specified index.  If the specified index is greater than
954     * <code>getNumElements() - 1</code>, the <code>numElements</code> property
955     * is increased to <code>index +1</code> and additional storage is allocated
956     * (if necessary) for the new element and all  (uninitialized) elements
957     * between the new element and the previous end of the array).
958     *
959     * @param index index to store a value in
960     * @param value value to store at the specified index
961     * @throws ArrayIndexOutOfBoundsException if {@code index < 0}.
962     */
963    public synchronized void setElement(int index, double value) {
964        if (index < 0) {
965            throw new ArrayIndexOutOfBoundsException(index);
966        }
967        if (index + 1 > numElements) {
968            numElements = index + 1;
969        }
970        if ((startIndex + index) >= internalArray.length) {
971            expandTo(startIndex + (index + 1));
972        }
973        internalArray[startIndex + index] = value;
974    }
975
976    /**
977     * Sets the expansionFactor.  Throws IllegalArgumentException if the
978     * the following conditions are not met:
979     * <ul>
980     * <li><code>expansionFactor > 1</code></li>
981     * <li><code>contractionFactor >= expansionFactor</code></li>
982     * </ul>
983     * @param expansionFactor the new expansion factor value.
984     * @throws MathIllegalArgumentException if expansionFactor is <= 1 or greater
985     * than contractionFactor
986     * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
987     */
988    @Deprecated
989    public void setExpansionFactor(float expansionFactor) throws MathIllegalArgumentException {
990        checkContractExpand(getContractionCriterion(), expansionFactor);
991        // The check above verifies that the expansion factor is > 1.0;
992        synchronized(this) {
993            this.expansionFactor = expansionFactor;
994        }
995    }
996
997    /**
998     * Sets the <code>expansionMode</code>. The specified value must be one of
999     * ADDITIVE_MODE, MULTIPLICATIVE_MODE.
1000     *
1001     * @param expansionMode The expansionMode to set.
1002     * @throws MathIllegalArgumentException if the specified mode value is not valid.
1003     * @deprecated As of 3.1. Please use {@link #setExpansionMode(ExpansionMode)} instead.
1004     */
1005    @Deprecated
1006    public void setExpansionMode(int expansionMode)
1007        throws MathIllegalArgumentException {
1008        if (expansionMode != MULTIPLICATIVE_MODE &&
1009            expansionMode != ADDITIVE_MODE) {
1010            throw new MathIllegalArgumentException(LocalizedFormats.UNSUPPORTED_EXPANSION_MODE, expansionMode,
1011                                                   MULTIPLICATIVE_MODE, "MULTIPLICATIVE_MODE",
1012                                                   ADDITIVE_MODE, "ADDITIVE_MODE");
1013        }
1014        synchronized(this) {
1015            if (expansionMode == MULTIPLICATIVE_MODE) {
1016                setExpansionMode(ExpansionMode.MULTIPLICATIVE);
1017            } else if (expansionMode == ADDITIVE_MODE) {
1018                setExpansionMode(ExpansionMode.ADDITIVE);
1019            }
1020        }
1021    }
1022
1023    /**
1024     * Sets the {@link ExpansionMode expansion mode}.
1025     *
1026     * @param expansionMode Expansion mode to use for resizing the array.
1027     * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
1028     */
1029    @Deprecated
1030    public void setExpansionMode(ExpansionMode expansionMode) {
1031        synchronized(this) {
1032            this.expansionMode = expansionMode;
1033        }
1034    }
1035
1036    /**
1037     * Sets the initial capacity.  Should only be invoked by constructors.
1038     *
1039     * @param initialCapacity of the array
1040     * @throws MathIllegalArgumentException if <code>initialCapacity</code> is not
1041     * positive.
1042     * @deprecated As of 3.1, this is a no-op.
1043     */
1044    @Deprecated
1045    protected void setInitialCapacity(int initialCapacity)
1046        throws MathIllegalArgumentException {
1047        // Body removed in 3.1.
1048    }
1049
1050    /**
1051     * This function allows you to control the number of elements contained
1052     * in this array, and can be used to "throw out" the last n values in an
1053     * array. This function will also expand the internal array as needed.
1054     *
1055     * @param i a new number of elements
1056     * @throws MathIllegalArgumentException if <code>i</code> is negative.
1057     */
1058    public synchronized void setNumElements(int i)
1059        throws MathIllegalArgumentException {
1060        // If index is negative thrown an error.
1061        if (i < 0) {
1062            throw new MathIllegalArgumentException(
1063                    LocalizedFormats.INDEX_NOT_POSITIVE,
1064                    i);
1065        }
1066
1067        // Test the new num elements, check to see if the array needs to be
1068        // expanded to accommodate this new number of elements.
1069        final int newSize = startIndex + i;
1070        if (newSize > internalArray.length) {
1071            expandTo(newSize);
1072        }
1073
1074        // Set the new number of elements to new value.
1075        numElements = i;
1076    }
1077
1078    /**
1079     * Returns true if the internal storage array has too many unused
1080     * storage positions.
1081     *
1082     * @return true if array satisfies the contraction criteria
1083     */
1084    private synchronized boolean shouldContract() {
1085        if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
1086            return (internalArray.length / ((float) numElements)) > contractionCriterion;
1087        } else {
1088            return (internalArray.length - numElements) > contractionCriterion;
1089        }
1090    }
1091
1092    /**
1093     * Returns the starting index of the internal array.  The starting index is
1094     * the position of the first addressable element in the internal storage
1095     * array.  The addressable elements in the array are <code>
1096     * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
1097     * </code>
1098     *
1099     * @return the starting index.
1100     * @deprecated As of 3.1.
1101     */
1102    @Deprecated
1103    public synchronized int start() {
1104        return startIndex;
1105    }
1106
1107    /**
1108     * <p>Copies source to dest, copying the underlying data, so dest is
1109     * a new, independent copy of source.  Does not contract before
1110     * the copy.</p>
1111     *
1112     * <p>Obtains synchronization locks on both source and dest
1113     * (in that order) before performing the copy.</p>
1114     *
1115     * <p>Neither source nor dest may be null; otherwise a {@link NullArgumentException}
1116     * is thrown</p>
1117     *
1118     * @param source ResizableDoubleArray to copy
1119     * @param dest ResizableArray to replace with a copy of the source array
1120     * @exception NullArgumentException if either source or dest is null
1121     * @since 2.0
1122     *
1123     */
1124    public static void copy(ResizableDoubleArray source,
1125                            ResizableDoubleArray dest)
1126        throws NullArgumentException {
1127        MathUtils.checkNotNull(source);
1128        MathUtils.checkNotNull(dest);
1129        synchronized(source) {
1130           synchronized(dest) {
1131               dest.contractionCriterion = source.contractionCriterion;
1132               dest.expansionFactor = source.expansionFactor;
1133               dest.expansionMode = source.expansionMode;
1134               dest.internalArray = new double[source.internalArray.length];
1135               System.arraycopy(source.internalArray, 0, dest.internalArray,
1136                       0, dest.internalArray.length);
1137               dest.numElements = source.numElements;
1138               dest.startIndex = source.startIndex;
1139           }
1140       }
1141    }
1142
1143    /**
1144     * Returns a copy of the ResizableDoubleArray.  Does not contract before
1145     * the copy, so the returned object is an exact copy of this.
1146     *
1147     * @return a new ResizableDoubleArray with the same data and configuration
1148     * properties as this
1149     * @since 2.0
1150     */
1151    public synchronized ResizableDoubleArray copy() {
1152        final ResizableDoubleArray result = new ResizableDoubleArray();
1153        copy(this, result);
1154        return result;
1155    }
1156
1157    /**
1158     * Returns true iff object is a ResizableDoubleArray with the same properties
1159     * as this and an identical internal storage array.
1160     *
1161     * @param object object to be compared for equality with this
1162     * @return true iff object is a ResizableDoubleArray with the same data and
1163     * properties as this
1164     * @since 2.0
1165     */
1166    @Override
1167    public boolean equals(Object object) {
1168        if (object == this ) {
1169            return true;
1170        }
1171        if (object instanceof ResizableDoubleArray == false) {
1172            return false;
1173        }
1174        synchronized(this) {
1175            synchronized(object) {
1176                boolean result = true;
1177                final ResizableDoubleArray other = (ResizableDoubleArray) object;
1178                result = result && (other.contractionCriterion == contractionCriterion);
1179                result = result && (other.expansionFactor == expansionFactor);
1180                result = result && (other.expansionMode == expansionMode);
1181                result = result && (other.numElements == numElements);
1182                result = result && (other.startIndex == startIndex);
1183                if (!result) {
1184                    return false;
1185                } else {
1186                    return Arrays.equals(internalArray, other.internalArray);
1187                }
1188            }
1189        }
1190    }
1191
1192    /**
1193     * Returns a hash code consistent with equals.
1194     *
1195     * @return the hash code representing this {@code ResizableDoubleArray}.
1196     * @since 2.0
1197     */
1198    @Override
1199    public synchronized int hashCode() {
1200        final int[] hashData = new int[6];
1201        hashData[0] = Double.valueOf(expansionFactor).hashCode();
1202        hashData[1] = Double.valueOf(contractionCriterion).hashCode();
1203        hashData[2] = expansionMode.hashCode();
1204        hashData[3] = Arrays.hashCode(internalArray);
1205        hashData[4] = numElements;
1206        hashData[5] = startIndex;
1207        return Arrays.hashCode(hashData);
1208    }
1209
1210}