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