summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/util/ResizableDoubleArray.java
blob: f0963efc4e1033fc173b9e43b95962ef13e67e99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.math3.util;

import org.apache.commons.math3.exception.MathIllegalArgumentException;
import org.apache.commons.math3.exception.MathIllegalStateException;
import org.apache.commons.math3.exception.MathInternalError;
import org.apache.commons.math3.exception.NotStrictlyPositiveException;
import org.apache.commons.math3.exception.NullArgumentException;
import org.apache.commons.math3.exception.NumberIsTooSmallException;
import org.apache.commons.math3.exception.util.LocalizedFormats;

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

/**
 * A variable length {@link DoubleArray} implementation that automatically handles expanding and
 * contracting its internal storage array as elements are added and removed.
 *
 * <h3>Important note: Usage should not assume that this class is thread-safe even though some of
 * the methods are {@code synchronized}. This qualifier will be dropped in the next major release
 * (4.0).</h3>
 *
 * <p>The internal storage array starts with capacity determined by the {@code initialCapacity}
 * property, which can be set by the constructor. The default initial capacity is 16. Adding
 * elements using {@link #addElement(double)} appends elements to the end of the array. When there
 * are no open entries at the end of the internal storage array, the array is expanded. The size of
 * the expanded array depends on the {@code expansionMode} and {@code expansionFactor} properties.
 * The {@code expansionMode} determines whether the size of the array is multiplied by the {@code
 * expansionFactor} ({@link ExpansionMode#MULTIPLICATIVE}) or if the expansion is additive ({@link
 * ExpansionMode#ADDITIVE} -- {@code expansionFactor} storage locations added). The default {@code
 * expansionMode} is {@code MULTIPLICATIVE} and the default {@code expansionFactor} is 2.
 *
 * <p>The {@link #addElementRolling(double)} method adds a new element to the end of the internal
 * storage array and adjusts the "usable window" of the internal array forward by one position
 * (effectively making what was the second element the first, and so on). Repeated activations of
 * this method (or activation of {@link #discardFrontElements(int)}) will effectively orphan the
 * storage locations at the beginning of the internal storage array. To reclaim this storage, each
 * time one of these methods is activated, the size of the internal storage array is compared to the
 * number of addressable elements (the {@code numElements} property) and if the difference is too
 * large, the internal array is contracted to size {@code numElements + 1}. The determination of
 * when the internal storage array is "too large" depends on the {@code expansionMode} and {@code
 * contractionFactor} properties. If the {@code expansionMode} is {@code MULTIPLICATIVE},
 * contraction is triggered when the ratio between storage array length and {@code numElements}
 * exceeds {@code contractionFactor.} If the {@code expansionMode} is {@code ADDITIVE}, the number
 * of excess storage locations is compared to {@code contractionFactor}.
 *
 * <p>To avoid cycles of expansions and contractions, the {@code expansionFactor} must not exceed
 * the {@code contractionFactor}. Constructors and mutators for both of these properties enforce
 * this requirement, throwing a {@code MathIllegalArgumentException} if it is violated.
 */
public class ResizableDoubleArray implements DoubleArray, Serializable {
    /**
     * Additive expansion mode.
     *
     * @deprecated As of 3.1. Please use {@link ExpansionMode#ADDITIVE} instead.
     */
    @Deprecated public static final int ADDITIVE_MODE = 1;

    /**
     * Multiplicative expansion mode.
     *
     * @deprecated As of 3.1. Please use {@link ExpansionMode#MULTIPLICATIVE} instead.
     */
    @Deprecated public static final int MULTIPLICATIVE_MODE = 0;

    /** Serializable version identifier. */
    private static final long serialVersionUID = -3485529955529426875L;

    /** Default value for initial capacity. */
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    /** Default value for array size modifier. */
    private static final double DEFAULT_EXPANSION_FACTOR = 2.0;

    /**
     * Default value for the difference between {@link #contractionCriterion} and {@link
     * #expansionFactor}.
     */
    private static final double DEFAULT_CONTRACTION_DELTA = 0.5;

    /**
     * The contraction criteria determines when the internal array will be contracted to fit the
     * number of elements contained in the element array + 1.
     */
    private double contractionCriterion = 2.5;

    /**
     * The expansion factor of the array. When the array needs to be expanded, the new array size
     * will be {@code internalArray.length * expansionFactor} if {@code expansionMode} is set to
     * MULTIPLICATIVE_MODE, or {@code internalArray.length + expansionFactor} if {@code
     * expansionMode} is set to ADDITIVE_MODE.
     */
    private double expansionFactor = 2.0;

    /**
     * Determines whether array expansion by {@code expansionFactor} is additive or multiplicative.
     */
    private ExpansionMode expansionMode = ExpansionMode.MULTIPLICATIVE;

    /** The internal storage array. */
    private double[] internalArray;

    /**
     * The number of addressable elements in the array. Note that this has nothing to do with the
     * length of the internal storage array.
     */
    private int numElements = 0;

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

    /**
     * Specification of expansion algorithm.
     *
     * @since 3.1
     */
    public enum ExpansionMode {
        /** Multiplicative expansion mode. */
        MULTIPLICATIVE,
        /** Additive expansion mode. */
        ADDITIVE
    }

    /**
     * Creates an instance with default properties.
     *
     * <ul>
     *   <li>{@code initialCapacity = 16}
     *   <li>{@code expansionMode = MULTIPLICATIVE}
     *   <li>{@code expansionFactor = 2.0}
     *   <li>{@code contractionCriterion = 2.5}
     * </ul>
     */
    public ResizableDoubleArray() {
        this(DEFAULT_INITIAL_CAPACITY);
    }

    /**
     * Creates an instance with the specified initial capacity. Other properties take default
     * values:
     *
     * <ul>
     *   <li>{@code expansionMode = MULTIPLICATIVE}
     *   <li>{@code expansionFactor = 2.0}
     *   <li>{@code contractionCriterion = 2.5}
     * </ul>
     *
     * @param initialCapacity Initial size of the internal storage array.
     * @throws MathIllegalArgumentException if {@code initialCapacity <= 0}.
     */
    public ResizableDoubleArray(int initialCapacity) throws MathIllegalArgumentException {
        this(initialCapacity, DEFAULT_EXPANSION_FACTOR);
    }

    /**
     * Creates an instance from an existing {@code double[]} with the initial capacity and
     * numElements corresponding to the size of the supplied {@code double[]} array. If the supplied
     * array is null, a new empty array with the default initial capacity will be created. The input
     * array is copied, not referenced. Other properties take default values:
     *
     * <ul>
     *   <li>{@code initialCapacity = 16}
     *   <li>{@code expansionMode = MULTIPLICATIVE}
     *   <li>{@code expansionFactor = 2.0}
     *   <li>{@code contractionCriterion = 2.5}
     * </ul>
     *
     * @param initialArray initial array
     * @since 2.2
     */
    public ResizableDoubleArray(double[] initialArray) {
        this(
                DEFAULT_INITIAL_CAPACITY,
                DEFAULT_EXPANSION_FACTOR,
                DEFAULT_CONTRACTION_DELTA + DEFAULT_EXPANSION_FACTOR,
                ExpansionMode.MULTIPLICATIVE,
                initialArray);
    }

    /**
     * Creates an instance with the specified initial capacity and expansion factor. The remaining
     * properties take default values:
     *
     * <ul>
     *   <li>{@code expansionMode = MULTIPLICATIVE}
     *   <li>{@code contractionCriterion = 0.5 + expansionFactor}
     * </ul>
     *
     * <br>
     * Throws IllegalArgumentException if the following conditions are not met:
     *
     * <ul>
     *   <li>{@code initialCapacity > 0}
     *   <li>{@code expansionFactor > 1}
     * </ul>
     *
     * @param initialCapacity Initial size of the internal storage array.
     * @param expansionFactor The array will be expanded based on this parameter.
     * @throws MathIllegalArgumentException if parameters are not valid.
     * @deprecated As of 3.1. Please use {@link #ResizableDoubleArray(int,double)} instead.
     */
    @Deprecated
    public ResizableDoubleArray(int initialCapacity, float expansionFactor)
            throws MathIllegalArgumentException {
        this(initialCapacity, (double) expansionFactor);
    }

    /**
     * Creates an instance with the specified initial capacity and expansion factor. The remaining
     * properties take default values:
     *
     * <ul>
     *   <li>{@code expansionMode = MULTIPLICATIVE}
     *   <li>{@code contractionCriterion = 0.5 + expansionFactor}
     * </ul>
     *
     * <br>
     * Throws IllegalArgumentException if the following conditions are not met:
     *
     * <ul>
     *   <li>{@code initialCapacity > 0}
     *   <li>{@code expansionFactor > 1}
     * </ul>
     *
     * @param initialCapacity Initial size of the internal storage array.
     * @param expansionFactor The array will be expanded based on this parameter.
     * @throws MathIllegalArgumentException if parameters are not valid.
     * @since 3.1
     */
    public ResizableDoubleArray(int initialCapacity, double expansionFactor)
            throws MathIllegalArgumentException {
        this(initialCapacity, expansionFactor, DEFAULT_CONTRACTION_DELTA + expansionFactor);
    }

    /**
     * Creates an instance with the specified initialCapacity, expansionFactor, and
     * contractionCriterion. The expansion mode will default to {@code MULTIPLICATIVE}. <br>
     * Throws IllegalArgumentException if the following conditions are not met:
     *
     * <ul>
     *   <li>{@code initialCapacity > 0}
     *   <li>{@code expansionFactor > 1}
     *   <li>{@code contractionCriterion >= expansionFactor}
     * </ul>
     *
     * @param initialCapacity Initial size of the internal storage array..
     * @param expansionFactor The array will be expanded based on this parameter.
     * @param contractionCriteria Contraction criteria.
     * @throws MathIllegalArgumentException if parameters are not valid.
     * @deprecated As of 3.1. Please use {@link #ResizableDoubleArray(int,double,double)} instead.
     */
    @Deprecated
    public ResizableDoubleArray(
            int initialCapacity, float expansionFactor, float contractionCriteria)
            throws MathIllegalArgumentException {
        this(initialCapacity, (double) expansionFactor, (double) contractionCriteria);
    }

    /**
     * Creates an instance with the specified initial capacity, expansion factor, and contraction
     * criteria. The expansion mode will default to {@code MULTIPLICATIVE}. <br>
     * Throws IllegalArgumentException if the following conditions are not met:
     *
     * <ul>
     *   <li>{@code initialCapacity > 0}
     *   <li>{@code expansionFactor > 1}
     *   <li>{@code contractionCriterion >= expansionFactor}
     * </ul>
     *
     * @param initialCapacity Initial size of the internal storage array..
     * @param expansionFactor The array will be expanded based on this parameter.
     * @param contractionCriterion Contraction criterion.
     * @throws MathIllegalArgumentException if the parameters are not valid.
     * @since 3.1
     */
    public ResizableDoubleArray(
            int initialCapacity, double expansionFactor, double contractionCriterion)
            throws MathIllegalArgumentException {
        this(
                initialCapacity,
                expansionFactor,
                contractionCriterion,
                ExpansionMode.MULTIPLICATIVE,
                null);
    }

    /**
     * Create a ResizableArray with the specified properties.
     *
     * <p>Throws IllegalArgumentException if the following conditions are not met:
     *
     * <ul>
     *   <li><code>initialCapacity > 0</code>
     *   <li><code>expansionFactor > 1</code>
     *   <li><code>contractionFactor >= expansionFactor</code>
     *   <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code>
     * </ul>
     *
     * @param initialCapacity the initial size of the internal storage array
     * @param expansionFactor the array will be expanded based on this parameter
     * @param contractionCriteria the contraction Criteria
     * @param expansionMode the expansion mode
     * @throws MathIllegalArgumentException if parameters are not valid
     * @deprecated As of 3.1. Please use {@link
     *     #ResizableDoubleArray(int,double,double,ExpansionMode,double[])} instead.
     */
    @Deprecated
    public ResizableDoubleArray(
            int initialCapacity,
            float expansionFactor,
            float contractionCriteria,
            int expansionMode)
            throws MathIllegalArgumentException {
        this(
                initialCapacity,
                expansionFactor,
                contractionCriteria,
                expansionMode == ADDITIVE_MODE
                        ? ExpansionMode.ADDITIVE
                        : ExpansionMode.MULTIPLICATIVE,
                null);
        // XXX Just ot retain the expected failure in a unit test.
        // With the new "enum", that test will become obsolete.
        setExpansionMode(expansionMode);
    }

    /**
     * Creates an instance with the specified properties. <br>
     * Throws MathIllegalArgumentException if the following conditions are not met:
     *
     * <ul>
     *   <li>{@code initialCapacity > 0}
     *   <li>{@code expansionFactor > 1}
     *   <li>{@code contractionCriterion >= expansionFactor}
     * </ul>
     *
     * @param initialCapacity Initial size of the internal storage array.
     * @param expansionFactor The array will be expanded based on this parameter.
     * @param contractionCriterion Contraction criteria.
     * @param expansionMode Expansion mode.
     * @param data Initial contents of the array.
     * @throws MathIllegalArgumentException if the parameters are not valid.
     */
    public ResizableDoubleArray(
            int initialCapacity,
            double expansionFactor,
            double contractionCriterion,
            ExpansionMode expansionMode,
            double... data)
            throws MathIllegalArgumentException {
        if (initialCapacity <= 0) {
            throw new NotStrictlyPositiveException(
                    LocalizedFormats.INITIAL_CAPACITY_NOT_POSITIVE, initialCapacity);
        }
        checkContractExpand(contractionCriterion, expansionFactor);

        this.expansionFactor = expansionFactor;
        this.contractionCriterion = contractionCriterion;
        this.expansionMode = expansionMode;
        internalArray = new double[initialCapacity];
        numElements = 0;
        startIndex = 0;

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

    /**
     * Copy constructor. Creates a new ResizableDoubleArray that is a deep, fresh copy of the
     * original. Needs to acquire synchronization lock on original. Original may not be null;
     * otherwise a {@link NullArgumentException} is thrown.
     *
     * @param original array to copy
     * @exception NullArgumentException if original is null
     * @since 2.0
     */
    public ResizableDoubleArray(ResizableDoubleArray original) throws NullArgumentException {
        MathUtils.checkNotNull(original);
        copy(original, this);
    }

    /**
     * Adds an element to the end of this expandable array.
     *
     * @param value Value to be added to end of array.
     */
    public synchronized void addElement(double value) {
        if (internalArray.length <= startIndex + numElements) {
            expand();
        }
        internalArray[startIndex + numElements++] = value;
    }

    /**
     * Adds several element to the end of this expandable array.
     *
     * @param values Values to be added to end of array.
     * @since 2.2
     */
    public synchronized void addElements(double[] values) {
        final double[] tempArray = new double[numElements + values.length + 1];
        System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
        System.arraycopy(values, 0, tempArray, numElements, values.length);
        internalArray = tempArray;
        startIndex = 0;
        numElements += values.length;
    }

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

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

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

        // Check the contraction criterion.
        if (shouldContract()) {
            contract();
        }
        return discarded;
    }

    /**
     * Substitutes <code>value</code> for the most recently added value. Returns the value that has
     * been replaced. If the array is empty (i.e. if {@link #numElements} is zero), an
     * IllegalStateException is thrown.
     *
     * @param value New value to substitute for the most recently added value
     * @return the value that has been replaced in the array.
     * @throws MathIllegalStateException if the array is empty
     * @since 2.0
     */
    public synchronized double substituteMostRecentElement(double value)
            throws MathIllegalStateException {
        if (numElements < 1) {
            throw new MathIllegalStateException(
                    LocalizedFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY);
        }

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

        internalArray[substIndex] = value;

        return discarded;
    }

    /**
     * Checks the expansion factor and the contraction criterion and throws an
     * IllegalArgumentException if the contractionCriteria is less than the expansionCriteria
     *
     * @param expansion factor to be checked
     * @param contraction criteria to be checked
     * @throws MathIllegalArgumentException if the contractionCriteria is less than the
     *     expansionCriteria.
     * @deprecated As of 3.1. Please use {@link #checkContractExpand(double,double)} instead.
     */
    @Deprecated
    protected void checkContractExpand(float contraction, float expansion)
            throws MathIllegalArgumentException {
        checkContractExpand((double) contraction, (double) expansion);
    }

    /**
     * Checks the expansion factor and the contraction criterion and raises an exception if the
     * contraction criterion is smaller than the expansion criterion.
     *
     * @param contraction Criterion to be checked.
     * @param expansion Factor to be checked.
     * @throws NumberIsTooSmallException if {@code contraction < expansion}.
     * @throws NumberIsTooSmallException if {@code contraction <= 1}.
     * @throws NumberIsTooSmallException if {@code expansion <= 1 }.
     * @since 3.1
     */
    protected void checkContractExpand(double contraction, double expansion)
            throws NumberIsTooSmallException {
        if (contraction < expansion) {
            final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, true);
            e.getContext()
                    .addMessage(
                            LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR,
                            contraction,
                            expansion);
            throw e;
        }

        if (contraction <= 1) {
            final NumberIsTooSmallException e =
                    new NumberIsTooSmallException(contraction, 1, false);
            e.getContext()
                    .addMessage(
                            LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE, contraction);
            throw e;
        }

        if (expansion <= 1) {
            final NumberIsTooSmallException e =
                    new NumberIsTooSmallException(contraction, 1, false);
            e.getContext()
                    .addMessage(LocalizedFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE, expansion);
            throw e;
        }
    }

    /** Clear the array contents, resetting the number of elements to zero. */
    public synchronized void clear() {
        numElements = 0;
        startIndex = 0;
    }

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

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

        // Reset the start index to zero
        startIndex = 0;
    }

    /**
     * Discards the <code>i</code> initial elements of the array. For example, if the array contains
     * the elements 1,2,3,4, invoking <code>discardFrontElements(2)</code> will cause the first two
     * elements to be discarded, leaving 3,4 in the array. Throws illegalArgumentException if i
     * exceeds numElements.
     *
     * @param i the number of elements to discard from the front of the array
     * @throws MathIllegalArgumentException if i is greater than numElements.
     * @since 2.0
     */
    public synchronized void discardFrontElements(int i) throws MathIllegalArgumentException {
        discardExtremeElements(i, true);
    }

    /**
     * Discards the <code>i</code> last elements of the array. For example, if the array contains
     * the elements 1,2,3,4, invoking <code>discardMostRecentElements(2)</code> will cause the last
     * two elements to be discarded, leaving 1,2 in the array. Throws illegalArgumentException if i
     * exceeds numElements.
     *
     * @param i the number of elements to discard from the end of the array
     * @throws MathIllegalArgumentException if i is greater than numElements.
     * @since 2.0
     */
    public synchronized void discardMostRecentElements(int i) throws MathIllegalArgumentException {
        discardExtremeElements(i, false);
    }

    /**
     * Discards the <code>i</code> first or last elements of the array, depending on the value of
     * <code>front</code>. For example, if the array contains the elements 1,2,3,4, invoking <code>
     * discardExtremeElements(2,false)</code> will cause the last two elements to be discarded,
     * leaving 1,2 in the array. For example, if the array contains the elements 1,2,3,4, invoking
     * <code>discardExtremeElements(2,true)</code> will cause the first two elements to be
     * discarded, leaving 3,4 in the array. Throws illegalArgumentException if i exceeds
     * numElements.
     *
     * @param i the number of elements to discard from the front/end of the array
     * @param front true if elements are to be discarded from the front of the array, false if
     *     elements are to be discarded from the end of the array
     * @throws MathIllegalArgumentException if i is greater than numElements.
     * @since 2.0
     */
    private synchronized void discardExtremeElements(int i, boolean front)
            throws MathIllegalArgumentException {
        if (i > numElements) {
            throw new MathIllegalArgumentException(
                    LocalizedFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY, i, numElements);
        } else if (i < 0) {
            throw new MathIllegalArgumentException(
                    LocalizedFormats.CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS, i);
        } else {
            // "Subtract" this number of discarded from numElements
            numElements -= i;
            if (front) {
                startIndex += i;
            }
        }
        if (shouldContract()) {
            contract();
        }
    }

    /**
     * Expands the internal storage array using the expansion factor.
     *
     * <p>if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, the new array size will be
     * <code>internalArray.length * expansionFactor.</code> If <code>expansionMode</code> is set to
     * ADDITIVE_MODE, the length after expansion will be <code>
     * internalArray.length + expansionFactor</code>
     */
    protected synchronized void expand() {
        // notice the use of FastMath.ceil(), this guarantees that we will always
        // have an array of at least currentSize + 1.   Assume that the
        // current initial capacity is 1 and the expansion factor
        // is 1.000000000000000001.  The newly calculated size will be
        // rounded up to 2 after the multiplication is performed.
        int newSize = 0;
        if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
            newSize = (int) FastMath.ceil(internalArray.length * expansionFactor);
        } else {
            newSize = (int) (internalArray.length + FastMath.round(expansionFactor));
        }
        final double[] tempArray = new double[newSize];

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

    /**
     * Expands the internal storage array to the specified size.
     *
     * @param size Size of the new internal storage array.
     */
    private synchronized void expandTo(int size) {
        final double[] tempArray = new double[size];
        // Copy and swap
        System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
        internalArray = tempArray;
    }

    /**
     * The contraction criteria defines when the internal array will contract to store only the
     * number of elements in the element array. If the <code>expansionMode</code> is <code>
     * MULTIPLICATIVE_MODE</code>, contraction is triggered when the ratio between storage array
     * length and <code>numElements</code> exceeds <code>contractionFactor</code>. If the <code>
     * expansionMode</code> is <code>ADDITIVE_MODE</code>, the number of excess storage locations is
     * compared to <code>contractionFactor.</code>
     *
     * @return the contraction criteria used to reclaim memory.
     * @deprecated As of 3.1. Please use {@link #getContractionCriterion()} instead.
     */
    @Deprecated
    public float getContractionCriteria() {
        return (float) getContractionCriterion();
    }

    /**
     * The contraction criterion defines when the internal array will contract to store only the
     * number of elements in the element array. If the <code>expansionMode</code> is <code>
     * MULTIPLICATIVE_MODE</code>, contraction is triggered when the ratio between storage array
     * length and <code>numElements</code> exceeds <code>contractionFactor</code>. If the <code>
     * expansionMode</code> is <code>ADDITIVE_MODE</code>, the number of excess storage locations is
     * compared to <code>contractionFactor.</code>
     *
     * @return the contraction criterion used to reclaim memory.
     * @since 3.1
     */
    public double getContractionCriterion() {
        return contractionCriterion;
    }

    /**
     * Returns the element at the specified index
     *
     * @param index index to fetch a value from
     * @return value stored at the specified index
     * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than zero or is greater
     *     than <code>getNumElements() - 1</code>.
     */
    public synchronized double getElement(int index) {
        if (index >= numElements) {
            throw new ArrayIndexOutOfBoundsException(index);
        } else if (index >= 0) {
            return internalArray[startIndex + index];
        } else {
            throw new ArrayIndexOutOfBoundsException(index);
        }
    }

    /**
     * Returns a double array containing the elements of this <code>ResizableArray</code>. This
     * method returns a copy, not a reference to the underlying array, so that changes made to the
     * returned array have no effect on this <code>ResizableArray.</code>
     *
     * @return the double array.
     */
    public synchronized double[] getElements() {
        final double[] elementArray = new double[numElements];
        System.arraycopy(internalArray, startIndex, elementArray, 0, numElements);
        return elementArray;
    }

    /**
     * The expansion factor controls the size of a new array when an array needs to be expanded. The
     * <code>expansionMode</code> determines whether the size of the array is multiplied by the
     * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if the expansion is additive
     * (ADDITIVE_MODE -- <code>expansionFactor</code> storage locations added). The default <code>
     * expansionMode</code> is MULTIPLICATIVE_MODE and the default <code>expansionFactor</code> is
     * 2.0.
     *
     * @return the expansion factor of this expandable double array
     * @deprecated As of 3.1. Return type will be changed to "double" in 4.0.
     */
    @Deprecated
    public float getExpansionFactor() {
        return (float) expansionFactor;
    }

    /**
     * The expansion mode determines whether the internal storage array grows additively or
     * multiplicatively when it is expanded.
     *
     * @return the expansion mode.
     * @deprecated As of 3.1. Return value to be changed to {@link ExpansionMode} in 4.0.
     */
    @Deprecated
    public int getExpansionMode() {
        synchronized (this) {
            switch (expansionMode) {
                case MULTIPLICATIVE:
                    return MULTIPLICATIVE_MODE;
                case ADDITIVE:
                    return ADDITIVE_MODE;
                default:
                    throw new MathInternalError(); // Should never happen.
            }
        }
    }

    /**
     * Notice the package scope on this method. This method is simply here for the JUnit test, it
     * allows us check if the expansion is working properly after a number of expansions. This is
     * not meant to be a part of the public interface of this class.
     *
     * @return the length of the internal storage array.
     * @deprecated As of 3.1. Please use {@link #getCapacity()} instead.
     */
    @Deprecated
    synchronized int getInternalLength() {
        return internalArray.length;
    }

    /**
     * Gets the currently allocated size of the internal data structure used for storing elements.
     * This is not to be confused with {@link #getNumElements() the number of elements actually
     * stored}.
     *
     * @return the length of the internal array.
     * @since 3.1
     */
    public int getCapacity() {
        return internalArray.length;
    }

    /**
     * Returns the number of elements currently in the array. Please note that this is different
     * from the length of the internal storage array.
     *
     * @return the number of elements.
     */
    public synchronized int getNumElements() {
        return numElements;
    }

    /**
     * Returns the internal storage array. Note that this method returns a reference to the internal
     * storage array, not a copy, and to correctly address elements of the array, the <code>
     * startIndex</code> is required (available via the {@link #start} method). This method should
     * only be used in cases where copying the internal array is not practical. The {@link
     * #getElements} method should be used in all other cases.
     *
     * @return the internal storage array used by this object
     * @since 2.0
     * @deprecated As of 3.1.
     */
    @Deprecated
    public synchronized double[] getInternalValues() {
        return internalArray;
    }

    /**
     * Provides <em>direct</em> access to the internal storage array. Please note that this method
     * returns a reference to this object's storage array, not a copy. <br>
     * To correctly address elements of the array, the "start index" is required (available via the
     * {@link #getStartIndex() getStartIndex} method. <br>
     * This method should only be used to avoid copying the internal array. The returned value
     * <em>must</em> be used for reading only; other uses could lead to this object becoming
     * inconsistent. <br>
     * The {@link #getElements} method has no such limitation since it returns a copy of this
     * array's addressable elements.
     *
     * @return the internal storage array used by this object.
     * @since 3.1
     */
    protected double[] getArrayRef() {
        return internalArray;
    }

    /**
     * Returns the "start index" of the internal array. This index is the position of the first
     * addressable element in the internal storage array. The addressable elements in the array are
     * at indices contained in the interval [{@link #getStartIndex()}, {@link #getStartIndex()} +
     * {@link #getNumElements()} - 1].
     *
     * @return the start index.
     * @since 3.1
     */
    protected int getStartIndex() {
        return startIndex;
    }

    /**
     * Sets the contraction criteria.
     *
     * @param contractionCriteria contraction criteria
     * @throws MathIllegalArgumentException if the contractionCriteria is less than the
     *     expansionCriteria.
     * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
     */
    @Deprecated
    public void setContractionCriteria(float contractionCriteria)
            throws MathIllegalArgumentException {
        checkContractExpand(contractionCriteria, getExpansionFactor());
        synchronized (this) {
            this.contractionCriterion = contractionCriteria;
        }
    }

    /**
     * Performs an operation on the addressable elements of the array.
     *
     * @param f Function to be applied on this array.
     * @return the result.
     * @since 3.1
     */
    public double compute(MathArrays.Function f) {
        final double[] array;
        final int start;
        final int num;
        synchronized (this) {
            array = internalArray;
            start = startIndex;
            num = numElements;
        }
        return f.evaluate(array, start, num);
    }

    /**
     * Sets the element at the specified index. If the specified index is greater than <code>
     * getNumElements() - 1</code>, the <code>numElements</code> property is increased to <code>
     * index +1</code> and additional storage is allocated (if necessary) for the new element and
     * all (uninitialized) elements between the new element and the previous end of the array).
     *
     * @param index index to store a value in
     * @param value value to store at the specified index
     * @throws ArrayIndexOutOfBoundsException if {@code index < 0}.
     */
    public synchronized void setElement(int index, double value) {
        if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        if (index + 1 > numElements) {
            numElements = index + 1;
        }
        if ((startIndex + index) >= internalArray.length) {
            expandTo(startIndex + (index + 1));
        }
        internalArray[startIndex + index] = value;
    }

    /**
     * Sets the expansionFactor. Throws IllegalArgumentException if the the following conditions are
     * not met:
     *
     * <ul>
     *   <li><code>expansionFactor > 1</code>
     *   <li><code>contractionFactor >= expansionFactor</code>
     * </ul>
     *
     * @param expansionFactor the new expansion factor value.
     * @throws MathIllegalArgumentException if expansionFactor is <= 1 or greater than
     *     contractionFactor
     * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
     */
    @Deprecated
    public void setExpansionFactor(float expansionFactor) throws MathIllegalArgumentException {
        checkContractExpand(getContractionCriterion(), expansionFactor);
        // The check above verifies that the expansion factor is > 1.0;
        synchronized (this) {
            this.expansionFactor = expansionFactor;
        }
    }

    /**
     * Sets the <code>expansionMode</code>. The specified value must be one of ADDITIVE_MODE,
     * MULTIPLICATIVE_MODE.
     *
     * @param expansionMode The expansionMode to set.
     * @throws MathIllegalArgumentException if the specified mode value is not valid.
     * @deprecated As of 3.1. Please use {@link #setExpansionMode(ExpansionMode)} instead.
     */
    @Deprecated
    public void setExpansionMode(int expansionMode) throws MathIllegalArgumentException {
        if (expansionMode != MULTIPLICATIVE_MODE && expansionMode != ADDITIVE_MODE) {
            throw new MathIllegalArgumentException(
                    LocalizedFormats.UNSUPPORTED_EXPANSION_MODE,
                    expansionMode,
                    MULTIPLICATIVE_MODE,
                    "MULTIPLICATIVE_MODE",
                    ADDITIVE_MODE,
                    "ADDITIVE_MODE");
        }
        synchronized (this) {
            if (expansionMode == MULTIPLICATIVE_MODE) {
                setExpansionMode(ExpansionMode.MULTIPLICATIVE);
            } else if (expansionMode == ADDITIVE_MODE) {
                setExpansionMode(ExpansionMode.ADDITIVE);
            }
        }
    }

    /**
     * Sets the {@link ExpansionMode expansion mode}.
     *
     * @param expansionMode Expansion mode to use for resizing the array.
     * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
     */
    @Deprecated
    public void setExpansionMode(ExpansionMode expansionMode) {
        synchronized (this) {
            this.expansionMode = expansionMode;
        }
    }

    /**
     * Sets the initial capacity. Should only be invoked by constructors.
     *
     * @param initialCapacity of the array
     * @throws MathIllegalArgumentException if <code>initialCapacity</code> is not positive.
     * @deprecated As of 3.1, this is a no-op.
     */
    @Deprecated
    protected void setInitialCapacity(int initialCapacity) throws MathIllegalArgumentException {
        // Body removed in 3.1.
    }

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

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

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

    /**
     * Returns true if the internal storage array has too many unused storage positions.
     *
     * @return true if array satisfies the contraction criteria
     */
    private synchronized boolean shouldContract() {
        if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
            return (internalArray.length / ((float) numElements)) > contractionCriterion;
        } else {
            return (internalArray.length - numElements) > contractionCriterion;
        }
    }

    /**
     * Returns the starting index of the internal array. The starting index is the position of the
     * first addressable element in the internal storage array. The addressable elements in the
     * array are <code>
     * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
     * </code>
     *
     * @return the starting index.
     * @deprecated As of 3.1.
     */
    @Deprecated
    public synchronized int start() {
        return startIndex;
    }

    /**
     * Copies source to dest, copying the underlying data, so dest is a new, independent copy of
     * source. Does not contract before the copy.
     *
     * <p>Obtains synchronization locks on both source and dest (in that order) before performing
     * the copy.
     *
     * <p>Neither source nor dest may be null; otherwise a {@link NullArgumentException} is thrown
     *
     * @param source ResizableDoubleArray to copy
     * @param dest ResizableArray to replace with a copy of the source array
     * @exception NullArgumentException if either source or dest is null
     * @since 2.0
     */
    public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest)
            throws NullArgumentException {
        MathUtils.checkNotNull(source);
        MathUtils.checkNotNull(dest);
        synchronized (source) {
            synchronized (dest) {
                dest.contractionCriterion = source.contractionCriterion;
                dest.expansionFactor = source.expansionFactor;
                dest.expansionMode = source.expansionMode;
                dest.internalArray = new double[source.internalArray.length];
                System.arraycopy(
                        source.internalArray, 0, dest.internalArray, 0, dest.internalArray.length);
                dest.numElements = source.numElements;
                dest.startIndex = source.startIndex;
            }
        }
    }

    /**
     * Returns a copy of the ResizableDoubleArray. Does not contract before the copy, so the
     * returned object is an exact copy of this.
     *
     * @return a new ResizableDoubleArray with the same data and configuration properties as this
     * @since 2.0
     */
    public synchronized ResizableDoubleArray copy() {
        final ResizableDoubleArray result = new ResizableDoubleArray();
        copy(this, result);
        return result;
    }

    /**
     * Returns true iff object is a ResizableDoubleArray with the same properties as this and an
     * identical internal storage array.
     *
     * @param object object to be compared for equality with this
     * @return true iff object is a ResizableDoubleArray with the same data and properties as this
     * @since 2.0
     */
    @Override
    public boolean equals(Object object) {
        if (object == this) {
            return true;
        }
        if (object instanceof ResizableDoubleArray == false) {
            return false;
        }
        synchronized (this) {
            synchronized (object) {
                boolean result = true;
                final ResizableDoubleArray other = (ResizableDoubleArray) object;
                result = result && (other.contractionCriterion == contractionCriterion);
                result = result && (other.expansionFactor == expansionFactor);
                result = result && (other.expansionMode == expansionMode);
                result = result && (other.numElements == numElements);
                result = result && (other.startIndex == startIndex);
                if (!result) {
                    return false;
                } else {
                    return Arrays.equals(internalArray, other.internalArray);
                }
            }
        }
    }

    /**
     * Returns a hash code consistent with equals.
     *
     * @return the hash code representing this {@code ResizableDoubleArray}.
     * @since 2.0
     */
    @Override
    public synchronized int hashCode() {
        final int[] hashData = new int[6];
        hashData[0] = Double.valueOf(expansionFactor).hashCode();
        hashData[1] = Double.valueOf(contractionCriterion).hashCode();
        hashData[2] = expansionMode.hashCode();
        hashData[3] = Arrays.hashCode(internalArray);
        hashData[4] = numElements;
        hashData[5] = startIndex;
        return Arrays.hashCode(hashData);
    }
}