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 org.hipparchus.Field;
17  import org.hipparchus.CalculusFieldElement;
18  import org.hipparchus.FieldElement;
19  import org.hipparchus.exception.LocalizedCoreFormats;
20  import org.hipparchus.exception.MathIllegalArgumentException;
21  import org.hipparchus.exception.MathRuntimeException;
22  import org.hipparchus.exception.NullArgumentException;
23  import org.hipparchus.random.RandomDataGenerator;
24  import org.hipparchus.util.MathUtils.FieldSumAndResidual;
25  import org.hipparchus.util.MathUtils.SumAndResidual;
26  import org.junit.Assert;
27  import org.junit.Test;
28  
29  /**
30   * Test cases for the MathUtils class.
31   */
32  public final class MathUtilsTest {
33      @Test
34      public void testEqualsDouble() {
35          final double x = 1234.5678;
36          Assert.assertTrue(MathUtils.equals(x, x));
37          Assert.assertFalse(MathUtils.equals(x, -x));
38  
39          // Special cases (cf. semantics of JDK's "Double").
40          // 1. NaN
41          Assert.assertTrue(MathUtils.equals(Double.NaN, Double.NaN));
42          // 2. Negative zero
43          final double mZero = -0d;
44          final double zero = 0d;
45          Assert.assertTrue(MathUtils.equals(zero, zero));
46          Assert.assertTrue(MathUtils.equals(mZero, mZero));
47          Assert.assertFalse(MathUtils.equals(mZero, zero));
48      }
49  
50      @Test
51      public void testHash() {
52          double[] testArray = {
53              Double.NaN,
54              Double.POSITIVE_INFINITY,
55              Double.NEGATIVE_INFINITY,
56              1d,
57              0d,
58              1E-14,
59              (1 + 1E-14),
60              Double.MIN_VALUE,
61              Double.MAX_VALUE };
62          for (int i = 0; i < testArray.length; i++) {
63              for (int j = 0; j < testArray.length; j++) {
64                  if (i == j) {
65                      Assert.assertEquals(MathUtils.hash(testArray[i]), MathUtils.hash(testArray[j]));
66                      Assert.assertEquals(MathUtils.hash(testArray[j]), MathUtils.hash(testArray[i]));
67                  } else {
68                      Assert.assertTrue(MathUtils.hash(testArray[i]) != MathUtils.hash(testArray[j]));
69                      Assert.assertTrue(MathUtils.hash(testArray[j]) != MathUtils.hash(testArray[i]));
70                  }
71              }
72          }
73      }
74  
75      @Test
76      public void testArrayHash() {
77          Assert.assertEquals(0, MathUtils.hash((double[]) null));
78          Assert.assertEquals(MathUtils.hash(new double[] {
79                                        Double.NaN, Double.POSITIVE_INFINITY,
80                                        Double.NEGATIVE_INFINITY, 1d, 0d
81                                      }),
82                       MathUtils.hash(new double[] {
83                                        Double.NaN, Double.POSITIVE_INFINITY,
84                                        Double.NEGATIVE_INFINITY, 1d, 0d
85                                      }));
86          Assert.assertFalse(MathUtils.hash(new double[] { 1d }) ==
87                      MathUtils.hash(new double[] { FastMath.nextAfter(1d, 2d) }));
88          Assert.assertFalse(MathUtils.hash(new double[] { 1d }) ==
89                      MathUtils.hash(new double[] { 1d, 1d }));
90      }
91  
92      /**
93       * Make sure that permuted arrays do not hash to the same value.
94       */
95      @Test
96      public void testPermutedArrayHash() {
97          double[] original = new double[10];
98          double[] permuted = new double[10];
99          RandomDataGenerator random = new RandomDataGenerator(100);
100 
101         // Generate 10 distinct random values
102         for (int i = 0; i < 10; i++) {
103             original[i] = random.nextUniform(i + 0.5, i + 0.75);
104         }
105 
106         // Generate a random permutation, making sure it is not the identity
107         boolean isIdentity = true;
108         do {
109             int[] permutation = random.nextPermutation(10, 10);
110             for (int i = 0; i < 10; i++) {
111                 if (i != permutation[i]) {
112                     isIdentity = false;
113                 }
114                 permuted[i] = original[permutation[i]];
115             }
116         } while (isIdentity);
117 
118         // Verify that permuted array has different hash
119         Assert.assertFalse(MathUtils.hash(original) == MathUtils.hash(permuted));
120     }
121 
122     @Test
123     public void testIndicatorByte() {
124         Assert.assertEquals((byte)1, MathUtils.copySign((byte)1, (byte)2));
125         Assert.assertEquals((byte)1, MathUtils.copySign((byte)1, (byte)0));
126         Assert.assertEquals((byte)(-1), MathUtils.copySign((byte)1, (byte)(-2)));
127     }
128 
129     @Test
130     public void testIndicatorInt() {
131         Assert.assertEquals(1, MathUtils.copySign(1, 2));
132         Assert.assertEquals(1, MathUtils.copySign(1, 0));
133         Assert.assertEquals((-1), MathUtils.copySign(1, -2));
134     }
135 
136     @Test
137     public void testIndicatorLong() {
138         Assert.assertEquals(1L, MathUtils.copySign(1L, 2L));
139         Assert.assertEquals(1L, MathUtils.copySign(1L, 0L));
140         Assert.assertEquals(-1L, MathUtils.copySign(1L, -2L));
141     }
142 
143     @Test
144     public void testIndicatorShort() {
145         Assert.assertEquals((short)1, MathUtils.copySign((short)1, (short)2));
146         Assert.assertEquals((short)1, MathUtils.copySign((short)1, (short)0));
147         Assert.assertEquals((short)(-1), MathUtils.copySign((short)1, (short)(-2)));
148     }
149 
150     @Test
151     public void testNormalizeAngle() {
152         for (double a = -15.0; a <= 15.0; a += 0.1) {
153             for (double b = -15.0; b <= 15.0; b += 0.2) {
154                 double c = MathUtils.normalizeAngle(a, b);
155                 Assert.assertTrue((b - FastMath.PI) <= c);
156                 Assert.assertTrue(c <= (b + FastMath.PI));
157                 double twoK = FastMath.rint((a - c) / FastMath.PI);
158                 Assert.assertEquals(c, a - twoK * FastMath.PI, 1.0e-14);
159             }
160         }
161     }
162 
163     @Test
164     public void testFieldNormalizeAngle() {
165         doTestFieldNormalizeAngle(Binary64Field.getInstance());
166     }
167 
168     private <T extends CalculusFieldElement<T>> void doTestFieldNormalizeAngle(final Field<T> field) {
169         final T zero = field.getZero();
170         for (double a = -15.0; a <= 15.0; a += 0.1) {
171             for (double b = -15.0; b <= 15.0; b += 0.2) {
172                 T c = MathUtils.normalizeAngle(zero.add(a), zero.add(b));
173                 double cR = c.getReal();
174                 Assert.assertTrue((b - FastMath.PI) <= cR);
175                 Assert.assertTrue(cR <= (b + FastMath.PI));
176                 double twoK = FastMath.rint((a - cR) / FastMath.PI);
177                 Assert.assertEquals(cR, a - twoK * FastMath.PI, 1.0e-14);
178             }
179         }
180     }
181 
182     @Test
183     public void testReduce() {
184         final double period = -12.222;
185         final double offset = 13;
186 
187         final double delta = 1.5;
188 
189         double orig = offset + 122456789 * period + delta;
190         double expected = delta;
191         Assert.assertEquals(expected,
192                             MathUtils.reduce(orig, period, offset),
193                             1e-7);
194         Assert.assertEquals(expected,
195                             MathUtils.reduce(orig, -period, offset),
196                             1e-7);
197 
198         orig = offset - 123356789 * period - delta;
199         expected = FastMath.abs(period) - delta;
200         Assert.assertEquals(expected,
201                             MathUtils.reduce(orig, period, offset),
202                             1e-6);
203         Assert.assertEquals(expected,
204                             MathUtils.reduce(orig, -period, offset),
205                             1e-6);
206 
207         orig = offset - 123446789 * period + delta;
208         expected = delta;
209         Assert.assertEquals(expected,
210                             MathUtils.reduce(orig, period, offset),
211                             1e-6);
212         Assert.assertEquals(expected,
213                             MathUtils.reduce(orig, -period, offset),
214                             1e-6);
215 
216         Assert.assertTrue(Double.isNaN(MathUtils.reduce(orig, Double.NaN, offset)));
217         Assert.assertTrue(Double.isNaN(MathUtils.reduce(Double.NaN, period, offset)));
218         Assert.assertTrue(Double.isNaN(MathUtils.reduce(orig, period, Double.NaN)));
219         Assert.assertTrue(Double.isNaN(MathUtils.reduce(orig, period,
220                 Double.POSITIVE_INFINITY)));
221         Assert.assertTrue(Double.isNaN(MathUtils.reduce(Double.POSITIVE_INFINITY,
222                 period, offset)));
223         Assert.assertTrue(Double.isNaN(MathUtils.reduce(orig,
224                 Double.POSITIVE_INFINITY, offset)));
225         Assert.assertTrue(Double.isNaN(MathUtils.reduce(orig,
226                 Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)));
227         Assert.assertTrue(Double.isNaN(MathUtils.reduce(Double.POSITIVE_INFINITY,
228                 period, Double.POSITIVE_INFINITY)));
229         Assert.assertTrue(Double.isNaN(MathUtils.reduce(Double.POSITIVE_INFINITY,
230                 Double.POSITIVE_INFINITY, offset)));
231         Assert.assertTrue(Double.isNaN(MathUtils.reduce(Double.POSITIVE_INFINITY,
232                 Double.POSITIVE_INFINITY,  Double.POSITIVE_INFINITY)));
233     }
234 
235     @Test
236     public void testReduceComparedWithNormalizeAngle() {
237         final double tol = Math.ulp(1d);
238         final double period = 2 * Math.PI;
239         for (double a = -15; a <= 15; a += 0.5) {
240             for (double center = -15; center <= 15; center += 1) {
241                 final double nA = MathUtils.normalizeAngle(a, center);
242                 final double offset = center - Math.PI;
243                 final double r = MathUtils.reduce(a, period, offset);
244                 Assert.assertEquals(nA, r + offset, tol);
245             }
246         }
247     }
248 
249     @Test
250     public void testSignByte() {
251         final byte one = (byte) 1;
252         Assert.assertEquals((byte) 1, MathUtils.copySign(one, (byte) 2));
253         Assert.assertEquals((byte) (-1), MathUtils.copySign(one, (byte) (-2)));
254     }
255 
256     @Test
257     public void testSignInt() {
258         final int one = 1;
259         Assert.assertEquals(1, MathUtils.copySign(one, 2));
260         Assert.assertEquals((-1), MathUtils.copySign(one, -2));
261     }
262 
263     @Test
264     public void testSignLong() {
265         final long one = 1L;
266         Assert.assertEquals(1L, MathUtils.copySign(one, 2L));
267         Assert.assertEquals(-1L, MathUtils.copySign(one, -2L));
268     }
269 
270     @Test
271     public void testSignShort() {
272         final short one = (short) 1;
273         Assert.assertEquals((short) 1, MathUtils.copySign(one, (short) 2));
274         Assert.assertEquals((short) (-1), MathUtils.copySign(one, (short) (-2)));
275     }
276 
277     @Test
278     public void testCheckFinite() {
279         try {
280             MathUtils.checkFinite(Double.POSITIVE_INFINITY);
281             Assert.fail("an exception should have been thrown");
282         } catch (MathIllegalArgumentException e) {
283             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
284         }
285         try {
286             MathUtils.checkFinite(Double.NEGATIVE_INFINITY);
287             Assert.fail("an exception should have been thrown");
288         } catch (MathIllegalArgumentException e) {
289             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
290         }
291         try {
292             MathUtils.checkFinite(Double.NaN);
293             Assert.fail("an exception should have been thrown");
294         } catch (MathIllegalArgumentException e) {
295             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
296         }
297 
298         try {
299             MathUtils.checkFinite(new double[] {0, -1, Double.POSITIVE_INFINITY, -2, 3});
300             Assert.fail("an exception should have been thrown");
301         } catch (MathIllegalArgumentException e) {
302             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
303         }
304         try {
305             MathUtils.checkFinite(new double[] {1, Double.NEGATIVE_INFINITY, -2, 3});
306             Assert.fail("an exception should have been thrown");
307         } catch (MathIllegalArgumentException e) {
308             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
309         }
310         try {
311             MathUtils.checkFinite(new double[] {4, 3, -1, Double.NaN, -2, 1});
312             Assert.fail("an exception should have been thrown");
313         } catch (MathIllegalArgumentException e) {
314             Assert.assertEquals(LocalizedCoreFormats.NOT_FINITE_NUMBER, e.getSpecifier());
315         }
316     }
317 
318     @Test
319     public void testCheckNotNull1() {
320         try {
321             Object obj = null;
322             MathUtils.checkNotNull(obj);
323         } catch (NullArgumentException e) {
324             // Expected.
325         }
326     }
327 
328     @Test
329     public void testCheckNotNull2() {
330         try {
331             double[] array = null;
332             MathUtils.checkNotNull(array, LocalizedCoreFormats.INPUT_ARRAY);
333         } catch (NullArgumentException e) {
334             // Expected.
335         }
336     }
337 
338     @Test
339     public void testCopySignByte() {
340         byte a = MathUtils.copySign(Byte.MIN_VALUE, (byte) -1);
341         Assert.assertEquals(Byte.MIN_VALUE, a);
342 
343         final byte minValuePlusOne = Byte.MIN_VALUE + (byte) 1;
344         a = MathUtils.copySign(minValuePlusOne, (byte) 1);
345         Assert.assertEquals(Byte.MAX_VALUE, a);
346 
347         a = MathUtils.copySign(Byte.MAX_VALUE, (byte) -1);
348         Assert.assertEquals(minValuePlusOne, a);
349 
350         final byte one = 1;
351         byte val = -2;
352         a = MathUtils.copySign(val, one);
353         Assert.assertEquals(-val, a);
354 
355         final byte minusOne = -one;
356         val = 2;
357         a = MathUtils.copySign(val, minusOne);
358         Assert.assertEquals(-val, a);
359 
360         val = 0;
361         a = MathUtils.copySign(val, minusOne);
362         Assert.assertEquals(val, a);
363 
364         val = 0;
365         a = MathUtils.copySign(val, one);
366         Assert.assertEquals(val, a);
367     }
368 
369     @Test(expected=MathRuntimeException.class)
370     public void testCopySignByte2() {
371         MathUtils.copySign(Byte.MIN_VALUE, (byte) 1);
372     }
373 
374     /**
375      * Tests {@link MathUtils#twoSum(double, double)}.
376      */
377     @Test
378     public void testTwoSum() {
379         //      sum = 0.30000000000000004
380         // residual = -2.7755575615628914E-17
381         final double a1 = 0.1;
382         final double b1 = 0.2;
383         final SumAndResidual result1 = MathUtils.twoSum(a1, b1);
384         Assert.assertEquals(a1 + b1, result1.getSum(), 0.);
385         Assert.assertEquals(a1 + b1, result1.getSum() + result1.getResidual(), 0.);
386         Assert.assertNotEquals(0., result1.getResidual(), 0.);
387 
388         //      sum = -1580.3205849419005
389         // residual = -1.1368683772161603E-13
390         final double a2 = -615.7212034581913;
391         final double b2 = -964.5993814837093;
392         final SumAndResidual result2 = MathUtils.twoSum(a2, b2);
393         Assert.assertEquals(a2 + b2, result2.getSum(), 0.);
394         Assert.assertEquals(a2 + b2, result2.getSum() + result2.getResidual(), 0.);
395         Assert.assertNotEquals(0., result2.getResidual(), 0.);
396 
397         //      sum = 251.8625825973395
398         // residual = 1.4210854715202004E-14
399         final double a3 = 60.348375484313706;
400         final double b3 = 191.5142071130258;
401         final SumAndResidual result3 = MathUtils.twoSum(a3, b3);
402         Assert.assertEquals(a3 + b3, result3.getSum(), 0.);
403         Assert.assertEquals(a3 + b3, result3.getSum() + result3.getResidual(), 0.);
404         Assert.assertNotEquals(0., result3.getResidual(), 0.);
405 
406         //      sum = 622.8319023175123
407         // residual = -4.3315557304163255E-14
408         final double a4 = 622.8314146170453;
409         final double b4 = 0.0004877004669900762;
410         final SumAndResidual result4 = MathUtils.twoSum(a4, b4);
411         Assert.assertEquals(a4 + b4, result4.getSum(), 0.);
412         Assert.assertEquals(a4 + b4, result4.getSum() + result4.getResidual(), 0.);
413         Assert.assertNotEquals(0., result4.getResidual(), 0.);
414     }
415 
416     /**
417      * Tests {@link MathUtils#twoSum(FieldElement, FieldElement)}.
418      */
419     @Test
420     public void testTwoSumField() {
421         final Tuple a = new Tuple(0.1, -615.7212034581913, 60.348375484313706, 622.8314146170453);
422         final Tuple b = new Tuple(0.2, -964.5993814837093, 191.5142071130258, 0.0004877004669900762);
423         final FieldSumAndResidual<Tuple> result = MathUtils.twoSum(a, b);
424         for (int i = 0; i < a.getDimension(); ++i) {
425             Assert.assertEquals(a.getComponent(i) + b.getComponent(i), result.getSum().getComponent(i), 0.);
426             Assert.assertEquals(a.getComponent(i) + b.getComponent(i),
427                     result.getSum().getComponent(i) + result.getResidual().getComponent(i), 0.);
428             Assert.assertNotEquals(0., result.getResidual().getComponent(i), 0.);
429         }
430     }
431 
432 }