View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to You under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    * https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
9    * or agreed to in writing, software distributed under the License is
10   * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11   * KIND, either express or implied. See the License for the specific language
12   * governing permissions and limitations under the License.
13   */
14  package org.hipparchus.util;
15  
16  import static org.junit.Assert.assertArrayEquals;
17  import static org.junit.Assert.assertEquals;
18  import static org.junit.Assert.assertFalse;
19  import static org.junit.Assert.assertTrue;
20  import static org.junit.Assert.fail;
21  
22  import java.util.Arrays;
23  
24  import org.hipparchus.exception.MathIllegalArgumentException;
25  import org.hipparchus.exception.NullArgumentException;
26  import org.hipparchus.random.RandomDataGenerator;
27  import org.hipparchus.util.ResizableDoubleArray.ExpansionMode;
28  import org.junit.After;
29  import org.junit.Before;
30  import org.junit.Test;
31  
32  /**
33   * This class contains test cases for the ResizableDoubleArray.
34   */
35  public class ResizableDoubleArrayTest {
36  
37      protected ResizableDoubleArray da = null;
38  
39      // Array used to test rolling
40      protected ResizableDoubleArray ra = null;
41  
42      @After
43      public void tearDown()
44          throws Exception {
45          da = null;
46          ra = null;
47      }
48  
49      @Before
50      public void setUp()
51          throws Exception {
52          da = new ResizableDoubleArray();
53          ra = new ResizableDoubleArray();
54      }
55  
56      @Test
57      public void testConstructors() {
58          float defaultExpansionFactor = 2.0f;
59          double defaultContractionCriteria = 2.5;
60          ExpansionMode defaultMode = ResizableDoubleArray.ExpansionMode.MULTIPLICATIVE;
61  
62          ResizableDoubleArray testDa = new ResizableDoubleArray(2);
63          assertEquals(0, testDa.getNumElements());
64          assertEquals(2, testDa.getCapacity());
65          assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
66          assertEquals(defaultContractionCriteria, testDa.getContractionCriterion(), 0);
67          assertEquals(defaultMode, testDa.getExpansionMode());
68          try {
69              da = new ResizableDoubleArray(-1);
70              fail("Expecting MathIllegalArgumentException");
71          } catch (MathIllegalArgumentException ex) {
72              // expected
73          }
74  
75          testDa = new ResizableDoubleArray((double[]) null);
76          assertEquals(0, testDa.getNumElements());
77  
78          double[] initialArray = new double[] { 0, 1, 2 };
79  
80          testDa = new ResizableDoubleArray(initialArray);
81          assertEquals(3, testDa.getNumElements());
82  
83          testDa = new ResizableDoubleArray(2, 2.0);
84          assertEquals(0, testDa.getNumElements());
85          assertEquals(2, testDa.getCapacity());
86          assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
87          assertEquals(defaultContractionCriteria, testDa.getContractionCriterion(), 0);
88          assertEquals(defaultMode, testDa.getExpansionMode());
89  
90          try {
91              da = new ResizableDoubleArray(2, 0.5);
92              fail("Expecting MathIllegalArgumentException");
93          } catch (MathIllegalArgumentException ex) {
94              // expected
95          }
96  
97          testDa = new ResizableDoubleArray(2, 3.0);
98          assertEquals(3.0f, testDa.getExpansionFactor(), 0);
99          assertEquals(3.5f, testDa.getContractionCriterion(), 0);
100 
101         testDa = new ResizableDoubleArray(2, 2.0, 3.0);
102         assertEquals(0, testDa.getNumElements());
103         assertEquals(2, testDa.getCapacity());
104         assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
105         assertEquals(3.0f, testDa.getContractionCriterion(), 0);
106         assertEquals(defaultMode, testDa.getExpansionMode());
107 
108         try {
109             da = new ResizableDoubleArray(2, 2.0, 1.5);
110             fail("Expecting MathIllegalArgumentException");
111         } catch (MathIllegalArgumentException ex) {
112             // expected
113         }
114 
115         testDa = new ResizableDoubleArray(2, 2.0, 3.0, ResizableDoubleArray.ExpansionMode.ADDITIVE);
116         assertEquals(0, testDa.getNumElements());
117         assertEquals(2, testDa.getCapacity());
118         assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
119         assertEquals(3.0f, testDa.getContractionCriterion(), 0);
120         assertEquals(ResizableDoubleArray.ExpansionMode.ADDITIVE, testDa.getExpansionMode());
121 
122         try {
123             da = new ResizableDoubleArray(2, 2.0d, 2.5d, null);
124             fail("Expecting NullArgumentException");
125         } catch (NullArgumentException ex) {
126             // expected
127         }
128 
129         // Copy constructor
130         testDa = new ResizableDoubleArray(2, 2.0, 3.0, ResizableDoubleArray.ExpansionMode.ADDITIVE);
131         testDa.addElement(2.0);
132         testDa.addElement(3.2);
133         ResizableDoubleArray copyDa = new ResizableDoubleArray(testDa);
134         assertEquals(copyDa, testDa);
135         assertEquals(testDa, copyDa);
136 
137         // JIRA: MATH-1252
138         final double[] values = { 1 };
139         testDa = new ResizableDoubleArray(values);
140         assertArrayEquals(values, testDa.getElements(), 0);
141         assertEquals(1, testDa.getNumElements());
142         assertEquals(1, testDa.getElement(0), 0);
143     }
144 
145     @Test
146     public void testGetValues() {
147         double[] controlArray = {
148             2.0, 4.0, 6.0
149         };
150 
151         da.addElement(2.0);
152         da.addElement(4.0);
153         da.addElement(6.0);
154 
155         double[] testArray = da.getElements();
156 
157         for (int i = 0; i < da.getNumElements(); i++) {
158             assertEquals("The testArray values should equal the controlArray values, " +
159                          "index i: " + i + " does not match", testArray[i],
160                          controlArray[i], Double.MIN_VALUE);
161         }
162     }
163 
164     @Test
165     public void testMinMax() {
166         da.addElement(2.0);
167         da.addElement(22.0);
168         da.addElement(-2.0);
169         da.addElement(21.0);
170         da.addElement(22.0);
171         da.addElement(42.0);
172         da.addElement(62.0);
173         da.addElement(22.0);
174         da.addElement(122.0);
175         da.addElement(1212.0);
176 
177         assertEquals("Min should be -2.0", -2.0,
178                      Arrays.stream(da.getElements()).min().getAsDouble(),
179                      Double.MIN_VALUE);
180 
181         assertEquals("Max should be 1212.0", 1212.0,
182                      Arrays.stream(da.getElements()).max().getAsDouble(),
183                      Double.MIN_VALUE);
184     }
185 
186     @Test
187     public void testSetElementArbitraryExpansion1() {
188 
189         // MULTIPLICATIVE_MODE
190         da.addElement(2.0);
191         da.addElement(4.0);
192         da.addElement(6.0);
193         da.setElement(1, 3.0);
194 
195         // Expand the array arbitrarily to 1000 items
196         da.setElement(1000, 3.4);
197 
198         assertEquals("The number of elements should now be 1001, it isn't",
199                      da.getNumElements(), 1001);
200 
201         assertEquals("Uninitialized Elements are default value of 0.0, index 766 wasn't",
202                      0.0, da.getElement(760), Double.MIN_VALUE);
203 
204         assertEquals("The 1000th index should be 3.4, it isn't", 3.4,
205                      da.getElement(1000), Double.MIN_VALUE);
206         assertEquals("The 0th index should be 2.0, it isn't", 2.0,
207                      da.getElement(0), Double.MIN_VALUE);
208     }
209 
210     @Test
211     public void testSetElementArbitraryExpansion2() {
212         // Make sure numElements and expansion work correctly for expansion
213         // boundary cases
214         da.addElement(2.0);
215         da.addElement(4.0);
216         da.addElement(6.0);
217         assertEquals(16, da.getCapacity());
218         assertEquals(3, da.getNumElements());
219         da.setElement(3, 7.0);
220         assertEquals(16, da.getCapacity());
221         assertEquals(4, da.getNumElements());
222         da.setElement(10, 10.0);
223         assertEquals(16, da.getCapacity());
224         assertEquals(11, da.getNumElements());
225         da.setElement(9, 10.0);
226         assertEquals(16, da.getCapacity());
227         assertEquals(11, da.getNumElements());
228 
229         try {
230             da.setElement(-2, 3);
231             fail("Expecting ArrayIndexOutOfBoundsException for negative index");
232         } catch (ArrayIndexOutOfBoundsException ex) {
233             // expected
234         }
235 
236         // ADDITIVE_MODE
237 
238         ResizableDoubleArray testDa =
239                 new ResizableDoubleArray(2, 2.0, 3.0, ResizableDoubleArray.ExpansionMode.ADDITIVE);
240         assertEquals(2, testDa.getCapacity());
241         testDa.addElement(1d);
242         testDa.addElement(1d);
243         assertEquals(2, testDa.getCapacity());
244         testDa.addElement(1d);
245         assertEquals(4, testDa.getCapacity());
246     }
247 
248     @Test
249     public void testAdd1000() {
250         for (int i = 0; i < 1000; i++) {
251             da.addElement(i);
252         }
253 
254         assertEquals("Number of elements should be equal to 1000 after adding 1000 values",
255                      1000, da.getNumElements());
256 
257         assertEquals("The element at the 56th index should be 56", 56.0,
258                      da.getElement(56), Double.MIN_VALUE);
259 
260         assertEquals("Internal Storage length should be 1024 if we started out with initial capacity of " +
261                      "16 and an expansion factor of 2.0", 1024,
262                      da.getCapacity());
263     }
264 
265     @Test
266     public void testAddElements() {
267         ResizableDoubleArray testDa = new ResizableDoubleArray();
268 
269         // MULTIPLICATIVE_MODE
270         testDa.addElements(new double[] { 4, 5, 6 });
271         assertEquals(3, testDa.getNumElements(), 0);
272         assertEquals(4, testDa.getElement(0), 0);
273         assertEquals(5, testDa.getElement(1), 0);
274         assertEquals(6, testDa.getElement(2), 0);
275 
276         testDa.addElements(new double[] { 4, 5, 6 });
277         assertEquals(6, testDa.getNumElements());
278 
279         // ADDITIVE_MODE (x's are occupied storage locations, 0's are open)
280         testDa = new ResizableDoubleArray(2, 2.0, 2.5, ResizableDoubleArray.ExpansionMode.ADDITIVE);
281         assertEquals(2, testDa.getCapacity());
282         testDa.addElements(new double[] { 1d }); // x,0
283         testDa.addElements(new double[] { 2d }); // x,x
284         testDa.addElements(new double[] { 3d }); // x,x,x,0 -- expanded
285         assertEquals(1d, testDa.getElement(0), 0);
286         assertEquals(2d, testDa.getElement(1), 0);
287         assertEquals(3d, testDa.getElement(2), 0);
288         assertEquals(4, testDa.getCapacity()); // x,x,x,0
289         assertEquals(3, testDa.getNumElements());
290     }
291 
292     @Test
293     public void testAddElementRolling() {
294         ra.addElement(0.5);
295         ra.addElement(1.0);
296         ra.addElement(1.0);
297         ra.addElement(1.0);
298         ra.addElement(1.0);
299         ra.addElement(1.0);
300         ra.addElementRolling(2.0);
301 
302         assertEquals("There should be 6 elements in the eda", 6,
303                      ra.getNumElements());
304         assertEquals("The max element should be 2.0", 2.0,
305                      Arrays.stream(ra.getElements()).max().getAsDouble(),
306                      Double.MIN_VALUE);
307         assertEquals("The min element should be 1.0", 1.0,
308                      Arrays.stream(ra.getElements()).min().getAsDouble(),
309                      Double.MIN_VALUE);
310 
311         for (int i = 0; i < 1024; i++) {
312             ra.addElementRolling(i);
313         }
314 
315         assertEquals("We just inserted 1024 rolling elements, num elements should still be 6",
316                      6, ra.getNumElements());
317 
318         // MULTIPLICATIVE_MODE
319         da.clear();
320         da.addElement(1);
321         da.addElement(2);
322         da.addElementRolling(3);
323         assertEquals(3, da.getElement(1), 0);
324         da.addElementRolling(4);
325         assertEquals(3, da.getElement(0), 0);
326         assertEquals(4, da.getElement(1), 0);
327         da.addElement(5);
328         assertEquals(5, da.getElement(2), 0);
329         da.addElementRolling(6);
330         assertEquals(4, da.getElement(0), 0);
331         assertEquals(5, da.getElement(1), 0);
332         assertEquals(6, da.getElement(2), 0);
333 
334         // ADDITIVE_MODE (x's are occupied storage locations, 0's are open)
335         ResizableDoubleArray testDa =
336                 new ResizableDoubleArray(2, 2.0, 2.5, ResizableDoubleArray.ExpansionMode.ADDITIVE);
337         assertEquals(2, testDa.getCapacity());
338         testDa.addElement(1d); // x,0
339         testDa.addElement(2d); // x,x
340         testDa.addElement(3d); // x,x,x,0 -- expanded
341         assertEquals(1d, testDa.getElement(0), 0);
342         assertEquals(2d, testDa.getElement(1), 0);
343         assertEquals(3d, testDa.getElement(2), 0);
344         assertEquals(4, testDa.getCapacity()); // x,x,x,0
345         assertEquals(3, testDa.getNumElements());
346         testDa.addElementRolling(4d);
347         assertEquals(2d, testDa.getElement(0), 0);
348         assertEquals(3d, testDa.getElement(1), 0);
349         assertEquals(4d, testDa.getElement(2), 0);
350         assertEquals(4, testDa.getCapacity()); // 0,x,x,x
351         assertEquals(3, testDa.getNumElements());
352         testDa.addElementRolling(5d); // 0,0,x,x,x,0 -- time to contract
353         assertEquals(3d, testDa.getElement(0), 0);
354         assertEquals(4d, testDa.getElement(1), 0);
355         assertEquals(5d, testDa.getElement(2), 0);
356         assertEquals(4, testDa.getCapacity()); // contracted -- x,x,x,0
357         assertEquals(3, testDa.getNumElements());
358         try {
359             testDa.getElement(4);
360             fail("Expecting ArrayIndexOutOfBoundsException");
361         } catch (ArrayIndexOutOfBoundsException ex) {
362             // expected
363         }
364         try {
365             testDa.getElement(-1);
366             fail("Expecting ArrayIndexOutOfBoundsException");
367         } catch (ArrayIndexOutOfBoundsException ex) {
368             // expected
369         }
370     }
371 
372     @Test
373     public void testSetNumberOfElements() {
374         da.addElement(1.0);
375         da.addElement(1.0);
376         da.addElement(1.0);
377         da.addElement(1.0);
378         da.addElement(1.0);
379         da.addElement(1.0);
380         assertEquals("Number of elements should equal 6", da.getNumElements(), 6);
381 
382         da.setNumElements(3);
383         assertEquals("Number of elements should equal 3", da.getNumElements(), 3);
384 
385         try {
386             da.setNumElements(-3);
387             fail("Setting number of elements to negative should've thrown an exception");
388         } catch (MathIllegalArgumentException iae) {
389         }
390 
391         da.setNumElements(1024);
392         assertEquals("Number of elements should now be 1024", da.getNumElements(), 1024);
393         assertEquals("Element 453 should be a default double", da.getElement(453), 0.0, Double.MIN_VALUE);
394     }
395 
396     @Test
397     public void testWithInitialCapacity() {
398 
399         ResizableDoubleArray eDA2 = new ResizableDoubleArray(2);
400         assertEquals("Initial number of elements should be 0", 0, eDA2.getNumElements());
401 
402         final RandomDataGenerator gen = new RandomDataGenerator(1000);
403         final int iterations = gen.nextInt(100, 1000);
404 
405         for (int i = 0; i < iterations; i++) {
406             eDA2.addElement(i);
407         }
408 
409         assertEquals("Number of elements should be equal to " + iterations,
410                      iterations, eDA2.getNumElements());
411 
412         eDA2.addElement(2.0);
413 
414         assertEquals("Number of elements should be equals to " +
415                      (iterations + 1), iterations + 1, eDA2.getNumElements());
416     }
417 
418     @Test
419     public void testWithInitialCapacityAndExpansionFactor() {
420 
421         ResizableDoubleArray eDA3 = new ResizableDoubleArray(3, 3.0, 3.5);
422         assertEquals("Initial number of elements should be 0", 0, eDA3.getNumElements());
423 
424         final RandomDataGenerator gen = new RandomDataGenerator(1000);
425         final int iterations = gen.nextInt(100, 1000);
426 
427         for (int i = 0; i < iterations; i++) {
428             eDA3.addElement(i);
429         }
430 
431         assertEquals("Number of elements should be equal to " + iterations,
432                      iterations, eDA3.getNumElements());
433 
434         eDA3.addElement(2.0);
435 
436         assertEquals("Number of elements should be equals to " +
437                      (iterations + 1), iterations + 1, eDA3.getNumElements());
438 
439         assertEquals("Expansion factor should equal 3.0", 3.0f,
440                      eDA3.getExpansionFactor(), Double.MIN_VALUE);
441     }
442 
443     @Test
444     public void testDiscard() {
445         da.addElement(2.0);
446         da.addElement(2.0);
447         da.addElement(2.0);
448         da.addElement(2.0);
449         da.addElement(2.0);
450         da.addElement(2.0);
451         da.addElement(2.0);
452         da.addElement(2.0);
453         da.addElement(2.0);
454         da.addElement(2.0);
455         da.addElement(2.0);
456         assertEquals("Number of elements should be 11", 11, da.getNumElements());
457 
458         da.discardFrontElements(5);
459         assertEquals("Number of elements should be 6", 6, da.getNumElements());
460 
461         da.addElement(2.0);
462         da.addElement(2.0);
463         da.addElement(2.0);
464         da.addElement(2.0);
465         assertEquals("Number of elements should be 10", 10, da.getNumElements());
466 
467         da.discardMostRecentElements(2);
468         assertEquals("Number of elements should be 8", 8, da.getNumElements());
469 
470         try {
471             da.discardFrontElements(-1);
472             fail("Trying to discard a negative number of element is not allowed");
473         } catch (Exception e) {
474         }
475 
476         try {
477             da.discardMostRecentElements(-1);
478             fail("Trying to discard a negative number of element is not allowed");
479         } catch (Exception e) {
480         }
481 
482         try {
483             da.discardFrontElements(10000);
484             fail("You can't discard more elements than the array contains");
485         } catch (Exception e) {
486         }
487 
488         try {
489             da.discardMostRecentElements(10000);
490             fail("You can't discard more elements than the array contains");
491         } catch (Exception e) {
492         }
493 
494     }
495 
496     @Test
497     public void testSubstitute() {
498 
499         da.addElement(2.0);
500         da.addElement(2.0);
501         da.addElement(2.0);
502         da.addElement(2.0);
503         da.addElement(2.0);
504         da.addElement(2.0);
505         da.addElement(2.0);
506         da.addElement(2.0);
507         da.addElement(2.0);
508         da.addElement(2.0);
509         da.addElement(2.0);
510         assertEquals("Number of elements should be 11", 11, da.getNumElements());
511 
512         da.substituteMostRecentElement(24);
513 
514         assertEquals("Number of elements should be 11", 11, da.getNumElements());
515 
516         try {
517             da.discardMostRecentElements(10);
518         } catch (Exception e) {
519             fail("Trying to discard a negative number of element is not allowed");
520         }
521 
522         da.substituteMostRecentElement(24);
523 
524         assertEquals("Number of elements should be 1", 1, da.getNumElements());
525 
526     }
527 
528     @SuppressWarnings("unlikely-arg-type")
529     @Test
530     public void testEqualsAndHashCode()
531         throws Exception {
532 
533         // Wrong type
534         ResizableDoubleArray first = new ResizableDoubleArray();
535         Double other = Double.valueOf(2);
536         assertFalse(first.equals(other));
537 
538         // Null
539         other = null;
540         assertFalse(first.equals(other));
541 
542         // Reflexive
543         assertTrue(first.equals(first));
544 
545         // Argumentless constructor
546         ResizableDoubleArray second = new ResizableDoubleArray();
547         verifyEquality(first, second);
548 
549         // Equals iff same data, same properties
550         ResizableDoubleArray third = new ResizableDoubleArray(3, 2.0, 2.0);
551         verifyInequality(third, first);
552         ResizableDoubleArray fourth = new ResizableDoubleArray(3, 2.0, 2.0);
553         ResizableDoubleArray fifth = new ResizableDoubleArray(2, 2.0, 2.0);
554         verifyEquality(third, fourth);
555         verifyInequality(third, fifth);
556         third.addElement(4.1);
557         third.addElement(4.2);
558         third.addElement(4.3);
559         fourth.addElement(4.1);
560         fourth.addElement(4.2);
561         fourth.addElement(4.3);
562         verifyEquality(third, fourth);
563 
564         // expand
565         fourth.addElement(4.4);
566         verifyInequality(third, fourth);
567         third.addElement(4.4);
568         verifyEquality(third, fourth);
569         fourth.addElement(4.4);
570         verifyInequality(third, fourth);
571         third.addElement(4.4);
572         verifyEquality(third, fourth);
573         fourth.addElementRolling(4.5);
574         third.addElementRolling(4.5);
575         verifyEquality(third, fourth);
576 
577         // discard
578         third.discardFrontElements(1);
579         verifyInequality(third, fourth);
580         fourth.discardFrontElements(1);
581         verifyEquality(third, fourth);
582 
583         // discard recent
584         third.discardMostRecentElements(2);
585         fourth.discardMostRecentElements(2);
586         verifyEquality(third, fourth);
587 
588         // wrong order
589         third.addElement(18);
590         fourth.addElement(17);
591         third.addElement(17);
592         fourth.addElement(18);
593         verifyInequality(third, fourth);
594 
595         // Copy constructor
596         verifyEquality(fourth, new ResizableDoubleArray(fourth));
597 
598         // Instance copy
599         verifyEquality(fourth, fourth.copy());
600     }
601 
602     @Test
603     public void testGetArrayRef() {
604         final ResizableDoubleArray a = new ResizableDoubleArray();
605 
606         // Modify "a" through the public API.
607         final int index = 20;
608         final double v1 = 1.2;
609         a.setElement(index, v1);
610 
611         // Modify the internal storage through the protected API.
612         final double v2 = v1 + 3.4;
613         final double[] aInternalArray = a.getArrayRef();
614         aInternalArray[a.getStartIndex() + index] = v2;
615 
616         assertEquals(v2, a.getElement(index), 0d);
617     }
618 
619     @Test
620     public void testCompute() {
621         final ResizableDoubleArray a = new ResizableDoubleArray();
622         final int max = 20;
623         for (int i = 1; i <= max; i++) {
624             a.setElement(i, i);
625         }
626 
627         final MathArrays.Function add = new MathArrays.Function() {
628 
629             @Override
630             public double evaluate(double[] a, int index, int num) {
631                 double sum = 0;
632                 final int max = index + num;
633                 for (int i = index; i < max; i++) {
634                     sum += a[i];
635                 }
636                 return sum;
637             }
638 
639             @Override
640             public double evaluate(double[] a) {
641                 return evaluate(a, 0, a.length);
642             }
643         };
644 
645         final double sum = a.compute(add);
646         assertEquals(0.5 * max * (max + 1), sum, 0);
647     }
648 
649     private void verifyEquality(ResizableDoubleArray a,
650                                 ResizableDoubleArray b) {
651         assertTrue(b.equals(a));
652         assertTrue(a.equals(b));
653         assertEquals(a.hashCode(), b.hashCode());
654     }
655 
656     private void verifyInequality(ResizableDoubleArray a,
657                                   ResizableDoubleArray b) {
658         assertFalse(b.equals(a));
659         assertFalse(a.equals(b));
660         assertFalse(a.hashCode() == b.hashCode());
661     }
662 
663 }