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
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.numbers.complex;
19  
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.List;
23  import java.util.function.BiFunction;
24  import java.util.function.DoubleFunction;
25  import java.util.function.Supplier;
26  
27  import org.apache.commons.rng.UniformRandomProvider;
28  import org.apache.commons.rng.simple.RandomSource;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.Disabled;
31  import org.junit.jupiter.api.Test;
32  
33  /**
34   * Tests for {@link Complex}.
35   *
36   * <p>Note: The ISO C99 math functions are not fully tested in this class. See also:
37   *
38   * <ul>
39   * <li>{@link CStandardTest} for a test of the ISO C99 standards including special case handling.
40   * <li>{@link CReferenceTest} for a test of the output using standard finite value against an
41   *     ISO C99 compliant reference implementation.
42   * <li>{@link ComplexEdgeCaseTest} for a test of extreme edge case finite values for real and/or
43   *     imaginary parts that can create intermediate overflow or underflow.
44   * </ul>
45   */
46  class ComplexTest {
47  
48      private static final double inf = Double.POSITIVE_INFINITY;
49      private static final double neginf = Double.NEGATIVE_INFINITY;
50      private static final double nan = Double.NaN;
51      private static final double pi = Math.PI;
52      private static final Complex oneInf = Complex.ofCartesian(1, inf);
53      private static final Complex oneNegInf = Complex.ofCartesian(1, neginf);
54      private static final Complex infOne = Complex.ofCartesian(inf, 1);
55      private static final Complex infZero = Complex.ofCartesian(inf, 0);
56      private static final Complex infNegZero = Complex.ofCartesian(inf, -0.0);
57      private static final Complex infNegInf = Complex.ofCartesian(inf, neginf);
58      private static final Complex infInf = Complex.ofCartesian(inf, inf);
59      private static final Complex negInfInf = Complex.ofCartesian(neginf, inf);
60      private static final Complex negInfOne = Complex.ofCartesian(neginf, 1);
61      private static final Complex negInfNegInf = Complex.ofCartesian(neginf, neginf);
62      private static final Complex oneNan = Complex.ofCartesian(1, nan);
63      private static final Complex zeroInf = Complex.ofCartesian(0, inf);
64      private static final Complex zeroNan = Complex.ofCartesian(0, nan);
65      private static final Complex nanZero = Complex.ofCartesian(nan, 0);
66      private static final Complex NAN = Complex.ofCartesian(nan, nan);
67      private static final Complex INF = Complex.ofCartesian(inf, inf);
68  
69      /**
70       * Used to test the number category of a Complex.
71       */
72      private enum NumberType {
73          NAN, INFINITE, FINITE
74      }
75  
76      /**
77       * Create a complex number given the real part.
78       *
79       * @param real Real part.
80       * @return {@code Complex} object
81       */
82      private static Complex ofReal(double real) {
83          return Complex.ofCartesian(real, 0);
84      }
85  
86      /**
87       * Create a complex number given the imaginary part.
88       *
89       * @param imaginary Imaginary part.
90       * @return {@code Complex} object
91       */
92      private static Complex ofImaginary(double imaginary) {
93          return Complex.ofCartesian(0, imaginary);
94      }
95  
96      @Test
97      @Disabled("Used to output the java environment")
98      @SuppressWarnings("squid:S2699")
99      void testJava() {
100         // CHECKSTYLE: stop Regexp
101         System.out.println(">>testJava()");
102         // MathTest#testExpSpecialCases() checks the following:
103         // Assert.assertEquals("exp of -infinity should be 0.0", 0.0,
104         // Math.exp(Double.NEGATIVE_INFINITY), Precision.EPSILON);
105         // Let's check how well Math works:
106         System.out.println("Math.exp=" + Math.exp(Double.NEGATIVE_INFINITY));
107         final String[] props = {"java.version", // Java Runtime Environment version
108             "java.vendor", // Java Runtime Environment vendor
109             "java.vm.specification.version", // Java Virtual Machine specification version
110             "java.vm.specification.vendor", // Java Virtual Machine specification vendor
111             "java.vm.specification.name", // Java Virtual Machine specification name
112             "java.vm.version", // Java Virtual Machine implementation version
113             "java.vm.vendor", // Java Virtual Machine implementation vendor
114             "java.vm.name", // Java Virtual Machine implementation name
115             "java.specification.version", // Java Runtime Environment specification
116                                           // version
117             "java.specification.vendor", // Java Runtime Environment specification vendor
118             "java.specification.name", // Java Runtime Environment specification name
119             "java.class.version", // Java class format version number
120         };
121         for (final String t : props) {
122             System.out.println(t + "=" + System.getProperty(t));
123         }
124         System.out.println("<<testJava()");
125         // CHECKSTYLE: resume Regexp
126     }
127 
128     @Test
129     void testCartesianConstructor() {
130         final Complex z = Complex.ofCartesian(3.0, 4.0);
131         Assertions.assertEquals(3.0, z.getReal());
132         Assertions.assertEquals(4.0, z.getImaginary());
133     }
134 
135     @Test
136     void testPolarConstructor() {
137         final double r = 98765;
138         final double theta = 0.12345;
139         final Complex z = Complex.ofPolar(r, theta);
140         final Complex y = Complex.ofCis(theta);
141         Assertions.assertEquals(r * y.getReal(), z.getReal());
142         Assertions.assertEquals(r * y.getImaginary(), z.getImaginary());
143 
144         // Edge cases
145         // Non-finite theta
146         Assertions.assertEquals(NAN, Complex.ofPolar(1, -inf));
147         Assertions.assertEquals(NAN, Complex.ofPolar(1, inf));
148         Assertions.assertEquals(NAN, Complex.ofPolar(1, nan));
149         // Infinite rho is invalid when theta is NaN
150         // i.e. do not create an infinite complex such as (inf, nan)
151         Assertions.assertEquals(NAN, Complex.ofPolar(inf, nan));
152         // negative or NaN rho
153         Assertions.assertEquals(NAN, Complex.ofPolar(-inf, 1));
154         Assertions.assertEquals(NAN, Complex.ofPolar(-0.0, 1));
155         Assertions.assertEquals(NAN, Complex.ofPolar(nan, 1));
156 
157         // Construction from infinity has values left to double arithmetic.
158         // Test the examples from the javadoc
159         Assertions.assertEquals(NAN, Complex.ofPolar(-0.0, 0.0));
160         Assertions.assertEquals(Complex.ofCartesian(0.0, 0.0), Complex.ofPolar(0.0, 0.0));
161         Assertions.assertEquals(Complex.ofCartesian(1.0, 0.0), Complex.ofPolar(1.0, 0.0));
162         Assertions.assertEquals(Complex.ofCartesian(-1.0, Math.sin(pi)), Complex.ofPolar(1.0, pi));
163         Assertions.assertEquals(Complex.ofCartesian(-inf, inf), Complex.ofPolar(inf, pi));
164         Assertions.assertEquals(Complex.ofCartesian(inf, nan), Complex.ofPolar(inf, 0.0));
165         Assertions.assertEquals(Complex.ofCartesian(inf, -inf), Complex.ofPolar(inf, -pi / 4));
166         Assertions.assertEquals(Complex.ofCartesian(-inf, -inf), Complex.ofPolar(inf, 5 * pi / 4));
167     }
168 
169     @Test
170     void testPolarConstructorAbsArg() {
171         // The test should work with any seed but use a fixed seed to avoid build
172         // instability.
173         final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create(678678638L);
174         for (int i = 0; i < 10; i++) {
175             final double rho = rng.nextDouble();
176             // Range (pi, pi]: lower exclusive, upper inclusive
177             final double theta = pi - rng.nextDouble() * 2 * pi;
178             final Complex z = Complex.ofPolar(rho, theta);
179             // Match within 1 ULP
180             Assertions.assertEquals(rho, z.abs(), Math.ulp(rho));
181             Assertions.assertEquals(theta, z.arg(), Math.ulp(theta));
182         }
183     }
184 
185     @Test
186     void testCisConstructor() {
187         final double x = 0.12345;
188         final Complex z = Complex.ofCis(x);
189         Assertions.assertEquals(Math.cos(x), z.getReal());
190         Assertions.assertEquals(Math.sin(x), z.getImaginary());
191     }
192 
193     /**
194      * Test parse and toString are compatible.
195      */
196     @Test
197     void testParseAndToString() {
198         final double[] parts = {Double.NEGATIVE_INFINITY, -1, -0.0, 0.0, 1, Math.PI, Double.POSITIVE_INFINITY,
199             Double.NaN};
200         for (final double x : parts) {
201             for (final double y : parts) {
202                 final Complex z = Complex.ofCartesian(x, y);
203                 Assertions.assertEquals(z, Complex.parse(z.toString()));
204             }
205         }
206         final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
207         for (int i = 0; i < 10; i++) {
208             final double x = -1 + rng.nextDouble() * 2;
209             final double y = -1 + rng.nextDouble() * 2;
210             final Complex z = Complex.ofCartesian(x, y);
211             Assertions.assertEquals(z, Complex.parse(z.toString()));
212         }
213 
214         // Special values not covered
215         Assertions.assertEquals(Complex.ofPolar(2, pi), Complex.parse(Complex.ofPolar(2, pi).toString()));
216         Assertions.assertEquals(Complex.ofCis(pi), Complex.parse(Complex.ofCis(pi).toString()));
217     }
218 
219     @Test
220     void testParseNull() {
221         Assertions.assertThrows(NullPointerException.class, () -> Complex.parse(null));
222     }
223 
224     @Test
225     void testParseEmpty() {
226         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse(""));
227         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse(" "));
228     }
229 
230     @Test
231     void testParseWrongStart() {
232         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("1.0,2.0)"));
233         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("[1.0,2.0)"));
234     }
235 
236     @Test
237     void testParseWrongEnd() {
238         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0,2.0"));
239         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0,2.0]"));
240     }
241 
242     @Test
243     void testParseWrongSeparator() {
244         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0 2.0)"));
245         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0:2.0)"));
246     }
247 
248     @Test
249     void testParseSeparatorOutsideStartAndEnd() {
250         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0,2.0),"));
251         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse(",(1.0,2.0)"));
252     }
253 
254     @Test
255     void testParseExtraSeparator() {
256         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0,,2.0)"));
257         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0,2.0,)"));
258         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(,1.0,2.0)"));
259         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0,2,0)"));
260     }
261 
262     @Test
263     void testParseInvalidRe() {
264         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(I.0,2.0)"));
265     }
266 
267     @Test
268     void testParseInvalidIm() {
269         Assertions.assertThrows(NumberFormatException.class, () -> Complex.parse("(1.0,2.G)"));
270     }
271 
272     @Test
273     void testParseSpaceAllowedAroundNumbers() {
274         final double re = 1.234;
275         final double im = 5.678;
276         final Complex z = Complex.ofCartesian(re, im);
277         Assertions.assertEquals(z, Complex.parse("(" + re + "," + im + ")"));
278         Assertions.assertEquals(z, Complex.parse("( " + re + "," + im + ")"));
279         Assertions.assertEquals(z, Complex.parse("(" + re + " ," + im + ")"));
280         Assertions.assertEquals(z, Complex.parse("(" + re + ", " + im + ")"));
281         Assertions.assertEquals(z, Complex.parse("(" + re + "," + im + " )"));
282         Assertions.assertEquals(z, Complex.parse("(  " + re + "  , " + im + "     )"));
283     }
284 
285     @Test
286     void testCGrammar() {
287         final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
288         for (int i = 0; i < 10; i++) {
289             final Complex z = Complex.ofCartesian(rng.nextDouble(), rng.nextDouble());
290             Assertions.assertEquals(z.getReal(), z.real(), "real");
291             Assertions.assertEquals(z.getImaginary(), z.imag(), "imag");
292         }
293     }
294 
295     @Test
296     void testAbs() {
297         final Complex z = Complex.ofCartesian(3.0, 4.0);
298         Assertions.assertEquals(5.0, z.abs());
299     }
300 
301     @Test
302     void testAbsNaN() {
303         // The result is NaN if either argument is NaN and the other is not infinite
304         Assertions.assertEquals(nan, NAN.abs());
305         Assertions.assertEquals(nan, Complex.ofCartesian(3.0, nan).abs());
306         Assertions.assertEquals(nan, Complex.ofCartesian(nan, 3.0).abs());
307         // The result is positive infinite if either argument is infinite
308         Assertions.assertEquals(inf, Complex.ofCartesian(inf, nan).abs());
309         Assertions.assertEquals(inf, Complex.ofCartesian(-inf, nan).abs());
310         Assertions.assertEquals(inf, Complex.ofCartesian(nan, inf).abs());
311         Assertions.assertEquals(inf, Complex.ofCartesian(nan, -inf).abs());
312         Assertions.assertEquals(inf, Complex.ofCartesian(inf, 3.0).abs());
313         Assertions.assertEquals(inf, Complex.ofCartesian(-inf, 3.0).abs());
314         Assertions.assertEquals(inf, Complex.ofCartesian(3.0, inf).abs());
315         Assertions.assertEquals(inf, Complex.ofCartesian(3.0, -inf).abs());
316     }
317 
318     /**
319      * Test standard values
320      */
321     @Test
322     void testArg() {
323         Complex z = Complex.ofCartesian(1, 0);
324         assertArgument(0.0, z, 1.0e-12);
325 
326         z = Complex.ofCartesian(1, 1);
327         assertArgument(Math.PI / 4, z, 1.0e-12);
328 
329         z = Complex.ofCartesian(0, 1);
330         assertArgument(Math.PI / 2, z, 1.0e-12);
331 
332         z = Complex.ofCartesian(-1, 1);
333         assertArgument(3 * Math.PI / 4, z, 1.0e-12);
334 
335         z = Complex.ofCartesian(-1, 0);
336         assertArgument(Math.PI, z, 1.0e-12);
337 
338         z = Complex.ofCartesian(-1, -1);
339         assertArgument(-3 * Math.PI / 4, z, 1.0e-12);
340 
341         z = Complex.ofCartesian(0, -1);
342         assertArgument(-Math.PI / 2, z, 1.0e-12);
343 
344         z = Complex.ofCartesian(1, -1);
345         assertArgument(-Math.PI / 4, z, 1.0e-12);
346     }
347 
348     /**
349      * Verify atan2-style handling of infinite parts
350      */
351     @Test
352     void testArgInf() {
353         assertArgument(Math.PI / 4, infInf, 1.0e-12);
354         assertArgument(Math.PI / 2, oneInf, 1.0e-12);
355         assertArgument(0.0, infOne, 1.0e-12);
356         assertArgument(Math.PI / 2, zeroInf, 1.0e-12);
357         assertArgument(0.0, infZero, 1.0e-12);
358         assertArgument(Math.PI, negInfOne, 1.0e-12);
359         assertArgument(-3.0 * Math.PI / 4, negInfNegInf, 1.0e-12);
360         assertArgument(-Math.PI / 2, oneNegInf, 1.0e-12);
361     }
362 
363     /**
364      * Verify that either part NaN results in NaN
365      */
366     @Test
367     void testArgNaN() {
368         assertArgument(Double.NaN, nanZero, 0);
369         assertArgument(Double.NaN, zeroNan, 0);
370         assertArgument(Double.NaN, NAN, 0);
371     }
372 
373     private static void assertArgument(double expected, Complex complex, double delta) {
374         final double actual = complex.arg();
375         Assertions.assertEquals(expected, actual, delta);
376         Assertions.assertEquals(actual, complex.arg(), delta);
377     }
378 
379     @Test
380     void testNorm() {
381         final Complex z = Complex.ofCartesian(3.0, 4.0);
382         Assertions.assertEquals(25.0, z.norm());
383     }
384 
385     @Test
386     void testNormNaN() {
387         // The result is NaN if either argument is NaN and the other is not infinite
388         Assertions.assertEquals(nan, NAN.norm());
389         Assertions.assertEquals(nan, Complex.ofCartesian(3.0, nan).norm());
390         Assertions.assertEquals(nan, Complex.ofCartesian(nan, 3.0).norm());
391         // The result is positive infinite if either argument is infinite
392         Assertions.assertEquals(inf, Complex.ofCartesian(inf, nan).norm());
393         Assertions.assertEquals(inf, Complex.ofCartesian(-inf, nan).norm());
394         Assertions.assertEquals(inf, Complex.ofCartesian(nan, inf).norm());
395         Assertions.assertEquals(inf, Complex.ofCartesian(nan, -inf).norm());
396     }
397 
398     /**
399      * Test all number types: isNaN, isInfinite, isFinite.
400      */
401     @Test
402     void testNumberType() {
403         assertNumberType(0, 0, NumberType.FINITE);
404         assertNumberType(1, 0, NumberType.FINITE);
405         assertNumberType(0, 1, NumberType.FINITE);
406 
407         assertNumberType(inf, 0, NumberType.INFINITE);
408         assertNumberType(-inf, 0, NumberType.INFINITE);
409         assertNumberType(0, inf, NumberType.INFINITE);
410         assertNumberType(0, -inf, NumberType.INFINITE);
411         // A complex or imaginary value with at least one infinite part is regarded as an
412         // infinity
413         // (even if its other part is a NaN).
414         assertNumberType(inf, nan, NumberType.INFINITE);
415         assertNumberType(-inf, nan, NumberType.INFINITE);
416         assertNumberType(nan, inf, NumberType.INFINITE);
417         assertNumberType(nan, -inf, NumberType.INFINITE);
418 
419         assertNumberType(nan, 0, NumberType.NAN);
420         assertNumberType(0, nan, NumberType.NAN);
421         assertNumberType(nan, nan, NumberType.NAN);
422     }
423 
424     /**
425      * Assert the number type of the Complex created from the real and imaginary
426      * components.
427      *
428      * @param real the real component
429      * @param imaginary the imaginary component
430      * @param type the type
431      */
432     private static void assertNumberType(double real, double imaginary, NumberType type) {
433         final Complex z = Complex.ofCartesian(real, imaginary);
434         final boolean isNaN = z.isNaN();
435         final boolean isInfinite = z.isInfinite();
436         final boolean isFinite = z.isFinite();
437         // A number can be only one
438         int count = isNaN ? 1 : 0;
439         count += isInfinite ? 1 : 0;
440         count += isFinite ? 1 : 0;
441         Assertions.assertEquals(1, count,
442             () -> String.format("Complex can be only one type: isNaN=%s, isInfinite=%s, isFinite=%s: %s", isNaN,
443                 isInfinite, isFinite, z));
444         switch (type) {
445         case FINITE:
446             Assertions.assertTrue(isFinite, () -> "not finite: " + z);
447             break;
448         case INFINITE:
449             Assertions.assertTrue(isInfinite, () -> "not infinite: " + z);
450             break;
451         case NAN:
452             Assertions.assertTrue(isNaN, () -> "not nan: " + z);
453             break;
454         default:
455             Assertions.fail("Unknown number type");
456         }
457     }
458 
459     @Test
460     void testConjugate() {
461         final Complex x = Complex.ofCartesian(3.0, 4.0);
462         final Complex z = x.conj();
463         Assertions.assertEquals(3.0, z.getReal());
464         Assertions.assertEquals(-4.0, z.getImaginary());
465     }
466 
467     @Test
468     void testConjugateNaN() {
469         final Complex z = NAN.conj();
470         Assertions.assertTrue(z.isNaN());
471     }
472 
473     @Test
474     void testConjugateInfinite() {
475         Complex z = Complex.ofCartesian(0, inf);
476         Assertions.assertEquals(neginf, z.conj().getImaginary());
477         z = Complex.ofCartesian(0, neginf);
478         Assertions.assertEquals(inf, z.conj().getImaginary());
479     }
480 
481     @Test
482     void testNegate() {
483         final Complex x = Complex.ofCartesian(3.0, 4.0);
484         final Complex z = x.negate();
485         Assertions.assertEquals(-3.0, z.getReal());
486         Assertions.assertEquals(-4.0, z.getImaginary());
487     }
488 
489     @Test
490     void testNegateNaN() {
491         final Complex z = NAN.negate();
492         Assertions.assertTrue(z.isNaN());
493     }
494 
495     @Test
496     void testProj() {
497         final Complex z = Complex.ofCartesian(3.0, 4.0);
498         Assertions.assertSame(z, z.proj());
499         // Sign must be the same for projection
500         TestUtils.assertSame(infZero, Complex.ofCartesian(inf, 4.0).proj());
501         TestUtils.assertSame(infZero, Complex.ofCartesian(inf, inf).proj());
502         TestUtils.assertSame(infZero, Complex.ofCartesian(inf, nan).proj());
503         TestUtils.assertSame(infZero, Complex.ofCartesian(3.0, inf).proj());
504         TestUtils.assertSame(infZero, Complex.ofCartesian(nan, inf).proj());
505         TestUtils.assertSame(infNegZero, Complex.ofCartesian(inf, -4.0).proj());
506         TestUtils.assertSame(infNegZero, Complex.ofCartesian(inf, -inf).proj());
507         TestUtils.assertSame(infNegZero, Complex.ofCartesian(3.0, -inf).proj());
508         TestUtils.assertSame(infNegZero, Complex.ofCartesian(nan, -inf).proj());
509     }
510 
511     @Test
512     void testAdd() {
513         final Complex x = Complex.ofCartesian(3.0, 4.0);
514         final Complex y = Complex.ofCartesian(5.0, 6.0);
515         final Complex z = x.add(y);
516         Assertions.assertEquals(8.0, z.getReal());
517         Assertions.assertEquals(10.0, z.getImaginary());
518     }
519 
520     @Test
521     void testAddInf() {
522         Complex x = Complex.ofCartesian(1, 1);
523         final Complex z = Complex.ofCartesian(inf, 0);
524         final Complex w = x.add(z);
525         Assertions.assertEquals(1, w.getImaginary());
526         Assertions.assertEquals(inf, w.getReal());
527 
528         x = Complex.ofCartesian(neginf, 0);
529         Assertions.assertTrue(Double.isNaN(x.add(z).getReal()));
530     }
531 
532     @Test
533     void testAddReal() {
534         final Complex x = Complex.ofCartesian(3.0, 4.0);
535         final double y = 5.0;
536         final Complex z = x.add(y);
537         Assertions.assertEquals(8.0, z.getReal());
538         Assertions.assertEquals(4.0, z.getImaginary());
539         // Equivalent
540         Assertions.assertEquals(z, x.add(ofReal(y)));
541     }
542 
543     @Test
544     void testAddRealNaN() {
545         final Complex x = Complex.ofCartesian(3.0, 4.0);
546         final double y = nan;
547         final Complex z = x.add(y);
548         Assertions.assertEquals(nan, z.getReal());
549         Assertions.assertEquals(4.0, z.getImaginary());
550         // Equivalent
551         Assertions.assertEquals(z, x.add(ofReal(y)));
552     }
553 
554     @Test
555     void testAddRealInf() {
556         final Complex x = Complex.ofCartesian(3.0, 4.0);
557         final double y = inf;
558         final Complex z = x.add(y);
559         Assertions.assertEquals(inf, z.getReal());
560         Assertions.assertEquals(4.0, z.getImaginary());
561         // Equivalent
562         Assertions.assertEquals(z, x.add(ofReal(y)));
563     }
564 
565     @Test
566     void testAddRealWithNegZeroImaginary() {
567         final Complex x = Complex.ofCartesian(3.0, -0.0);
568         final double y = 5.0;
569         final Complex z = x.add(y);
570         Assertions.assertEquals(8.0, z.getReal());
571         Assertions.assertEquals(-0.0, z.getImaginary(), "Expected sign preservation");
572         // Sign-preservation is a problem: -0.0 + 0.0 == 0.0
573         final Complex z2 = x.add(ofReal(y));
574         Assertions.assertEquals(8.0, z2.getReal());
575         Assertions.assertEquals(0.0, z2.getImaginary(), "Expected no-sign preservation");
576     }
577 
578     @Test
579     void testAddImaginary() {
580         final Complex x = Complex.ofCartesian(3.0, 4.0);
581         final double y = 5.0;
582         final Complex z = x.addImaginary(y);
583         Assertions.assertEquals(3.0, z.getReal());
584         Assertions.assertEquals(9.0, z.getImaginary());
585         // Equivalent
586         Assertions.assertEquals(z, x.add(ofImaginary(y)));
587     }
588 
589     @Test
590     void testAddImaginaryNaN() {
591         final Complex x = Complex.ofCartesian(3.0, 4.0);
592         final double y = nan;
593         final Complex z = x.addImaginary(y);
594         Assertions.assertEquals(3.0, z.getReal());
595         Assertions.assertEquals(nan, z.getImaginary());
596         // Equivalent
597         Assertions.assertEquals(z, x.add(ofImaginary(y)));
598     }
599 
600     @Test
601     void testAddImaginaryInf() {
602         final Complex x = Complex.ofCartesian(3.0, 4.0);
603         final double y = inf;
604         final Complex z = x.addImaginary(y);
605         Assertions.assertEquals(3.0, z.getReal());
606         Assertions.assertEquals(inf, z.getImaginary());
607         // Equivalent
608         Assertions.assertEquals(z, x.add(ofImaginary(y)));
609     }
610 
611     @Test
612     void testAddImaginaryWithNegZeroReal() {
613         final Complex x = Complex.ofCartesian(-0.0, 4.0);
614         final double y = 5.0;
615         final Complex z = x.addImaginary(y);
616         Assertions.assertEquals(-0.0, z.getReal(), "Expected sign preservation");
617         Assertions.assertEquals(9.0, z.getImaginary());
618         // Sign-preservation is a problem: -0.0 + 0.0 == 0.0
619         final Complex z2 = x.add(ofImaginary(y));
620         Assertions.assertEquals(0.0, z2.getReal(), "Expected no-sign preservation");
621         Assertions.assertEquals(9.0, z2.getImaginary());
622     }
623 
624     @Test
625     void testSubtract() {
626         final Complex x = Complex.ofCartesian(3.0, 4.0);
627         final Complex y = Complex.ofCartesian(5.0, 7.0);
628         final Complex z = x.subtract(y);
629         Assertions.assertEquals(-2.0, z.getReal());
630         Assertions.assertEquals(-3.0, z.getImaginary());
631     }
632 
633     @Test
634     void testSubtractInf() {
635         final Complex x = Complex.ofCartesian(3.0, 4.0);
636         final Complex y = Complex.ofCartesian(inf, 7.0);
637         Complex z = x.subtract(y);
638         Assertions.assertEquals(neginf, z.getReal());
639         Assertions.assertEquals(-3.0, z.getImaginary());
640 
641         z = y.subtract(y);
642         Assertions.assertEquals(nan, z.getReal());
643         Assertions.assertEquals(0.0, z.getImaginary());
644     }
645 
646     @Test
647     void testSubtractReal() {
648         final Complex x = Complex.ofCartesian(3.0, 4.0);
649         final double y = 5.0;
650         final Complex z = x.subtract(y);
651         Assertions.assertEquals(-2.0, z.getReal());
652         Assertions.assertEquals(4.0, z.getImaginary());
653         // Equivalent
654         Assertions.assertEquals(z, x.subtract(ofReal(y)));
655     }
656 
657     @Test
658     void testSubtractRealNaN() {
659         final Complex x = Complex.ofCartesian(3.0, 4.0);
660         final double y = nan;
661         final Complex z = x.subtract(y);
662         Assertions.assertEquals(nan, z.getReal());
663         Assertions.assertEquals(4.0, z.getImaginary());
664         // Equivalent
665         Assertions.assertEquals(z, x.subtract(ofReal(y)));
666     }
667 
668     @Test
669     void testSubtractRealInf() {
670         final Complex x = Complex.ofCartesian(3.0, 4.0);
671         final double y = inf;
672         final Complex z = x.subtract(y);
673         Assertions.assertEquals(-inf, z.getReal());
674         Assertions.assertEquals(4.0, z.getImaginary());
675         // Equivalent
676         Assertions.assertEquals(z, x.subtract(ofReal(y)));
677     }
678 
679     @Test
680     void testSubtractRealWithNegZeroImaginary() {
681         final Complex x = Complex.ofCartesian(3.0, -0.0);
682         final double y = 5.0;
683         final Complex z = x.subtract(y);
684         Assertions.assertEquals(-2.0, z.getReal());
685         Assertions.assertEquals(-0.0, z.getImaginary());
686         // Equivalent
687         // Sign-preservation is not a problem: -0.0 - 0.0 == -0.0
688         Assertions.assertEquals(z, x.subtract(ofReal(y)));
689     }
690 
691     @Test
692     void testSubtractImaginary() {
693         final Complex x = Complex.ofCartesian(3.0, 4.0);
694         final double y = 5.0;
695         final Complex z = x.subtractImaginary(y);
696         Assertions.assertEquals(3.0, z.getReal());
697         Assertions.assertEquals(-1.0, z.getImaginary());
698         // Equivalent
699         Assertions.assertEquals(z, x.subtract(ofImaginary(y)));
700     }
701 
702     @Test
703     void testSubtractImaginaryNaN() {
704         final Complex x = Complex.ofCartesian(3.0, 4.0);
705         final double y = nan;
706         final Complex z = x.subtractImaginary(y);
707         Assertions.assertEquals(3.0, z.getReal());
708         Assertions.assertEquals(nan, z.getImaginary());
709         // Equivalent
710         Assertions.assertEquals(z, x.subtract(ofImaginary(y)));
711     }
712 
713     @Test
714     void testSubtractImaginaryInf() {
715         final Complex x = Complex.ofCartesian(3.0, 4.0);
716         final double y = inf;
717         final Complex z = x.subtractImaginary(y);
718         Assertions.assertEquals(3.0, z.getReal());
719         Assertions.assertEquals(-inf, z.getImaginary());
720         // Equivalent
721         Assertions.assertEquals(z, x.subtract(ofImaginary(y)));
722     }
723 
724     @Test
725     void testSubtractImaginaryWithNegZeroReal() {
726         final Complex x = Complex.ofCartesian(-0.0, 4.0);
727         final double y = 5.0;
728         final Complex z = x.subtractImaginary(y);
729         Assertions.assertEquals(-0.0, z.getReal());
730         Assertions.assertEquals(-1.0, z.getImaginary());
731         // Equivalent
732         // Sign-preservation is not a problem: -0.0 - 0.0 == -0.0
733         Assertions.assertEquals(z, x.subtract(ofImaginary(y)));
734     }
735 
736     @Test
737     void testSubtractFromReal() {
738         final Complex x = Complex.ofCartesian(3.0, 4.0);
739         final double y = 5.0;
740         final Complex z = x.subtractFrom(y);
741         Assertions.assertEquals(2.0, z.getReal());
742         Assertions.assertEquals(-4.0, z.getImaginary());
743         // Equivalent
744         Assertions.assertEquals(z, ofReal(y).subtract(x));
745     }
746 
747     @Test
748     void testSubtractFromRealNaN() {
749         final Complex x = Complex.ofCartesian(3.0, 4.0);
750         final double y = nan;
751         final Complex z = x.subtractFrom(y);
752         Assertions.assertEquals(nan, z.getReal());
753         Assertions.assertEquals(-4.0, z.getImaginary());
754         // Equivalent
755         Assertions.assertEquals(z, ofReal(y).subtract(x));
756     }
757 
758     @Test
759     void testSubtractFromRealInf() {
760         final Complex x = Complex.ofCartesian(3.0, 4.0);
761         final double y = inf;
762         final Complex z = x.subtractFrom(y);
763         Assertions.assertEquals(inf, z.getReal());
764         Assertions.assertEquals(-4.0, z.getImaginary());
765         // Equivalent
766         Assertions.assertEquals(z, ofReal(y).subtract(x));
767     }
768 
769     @Test
770     void testSubtractFromRealWithPosZeroImaginary() {
771         final Complex x = Complex.ofCartesian(3.0, 0.0);
772         final double y = 5.0;
773         final Complex z = x.subtractFrom(y);
774         Assertions.assertEquals(2.0, z.getReal());
775         Assertions.assertEquals(-0.0, z.getImaginary(), "Expected sign inversion");
776         // Sign-inversion is a problem: 0.0 - 0.0 == 0.0
777         Assertions.assertNotEquals(z, ofReal(y).subtract(x));
778     }
779 
780     @Test
781     void testSubtractFromImaginary() {
782         final Complex x = Complex.ofCartesian(3.0, 4.0);
783         final double y = 5.0;
784         final Complex z = x.subtractFromImaginary(y);
785         Assertions.assertEquals(-3.0, z.getReal());
786         Assertions.assertEquals(1.0, z.getImaginary());
787         // Equivalent
788         Assertions.assertEquals(z, ofImaginary(y).subtract(x));
789     }
790 
791     @Test
792     void testSubtractFromImaginaryNaN() {
793         final Complex x = Complex.ofCartesian(3.0, 4.0);
794         final double y = nan;
795         final Complex z = x.subtractFromImaginary(y);
796         Assertions.assertEquals(-3.0, z.getReal());
797         Assertions.assertEquals(nan, z.getImaginary());
798         // Equivalent
799         Assertions.assertEquals(z, ofImaginary(y).subtract(x));
800     }
801 
802     @Test
803     void testSubtractFromImaginaryInf() {
804         final Complex x = Complex.ofCartesian(3.0, 4.0);
805         final double y = inf;
806         final Complex z = x.subtractFromImaginary(y);
807         Assertions.assertEquals(-3.0, z.getReal());
808         Assertions.assertEquals(inf, z.getImaginary());
809         // Equivalent
810         Assertions.assertEquals(z, ofImaginary(y).subtract(x));
811     }
812 
813     @Test
814     void testSubtractFromImaginaryWithPosZeroReal() {
815         final Complex x = Complex.ofCartesian(0.0, 4.0);
816         final double y = 5.0;
817         final Complex z = x.subtractFromImaginary(y);
818         Assertions.assertEquals(-0.0, z.getReal(), "Expected sign inversion");
819         Assertions.assertEquals(1.0, z.getImaginary());
820         // Sign-inversion is a problem: 0.0 - 0.0 == 0.0
821         Assertions.assertNotEquals(z, ofImaginary(y).subtract(x));
822     }
823 
824     @Test
825     void testMultiply() {
826         final Complex x = Complex.ofCartesian(3.0, 4.0);
827         final Complex y = Complex.ofCartesian(5.0, 6.0);
828         final Complex z = x.multiply(y);
829         Assertions.assertEquals(-9.0, z.getReal());
830         Assertions.assertEquals(38.0, z.getImaginary());
831     }
832 
833     @Test
834     void testMultiplyInfInf() {
835         final Complex z = infInf.multiply(infInf);
836         // Assert.assertTrue(z.isNaN()); // MATH-620
837         Assertions.assertTrue(z.isInfinite());
838 
839         // Expected results from g++:
840         Assertions.assertEquals(Complex.ofCartesian(nan, inf), infInf.multiply(infInf));
841         Assertions.assertEquals(Complex.ofCartesian(inf, nan), infInf.multiply(infNegInf));
842         Assertions.assertEquals(Complex.ofCartesian(-inf, nan), infInf.multiply(negInfInf));
843         Assertions.assertEquals(Complex.ofCartesian(nan, -inf), infInf.multiply(negInfNegInf));
844     }
845 
846     @Test
847     void testMultiplyReal() {
848         final Complex x = Complex.ofCartesian(3.0, 4.0);
849         final double y = 2.0;
850         Complex z = x.multiply(y);
851         Assertions.assertEquals(6.0, z.getReal());
852         Assertions.assertEquals(8.0, z.getImaginary());
853         // Equivalent
854         Assertions.assertEquals(z, x.multiply(ofReal(y)));
855 
856         z = x.multiply(-y);
857         Assertions.assertEquals(-6.0, z.getReal());
858         Assertions.assertEquals(-8.0, z.getImaginary());
859         // Equivalent
860         Assertions.assertEquals(z, x.multiply(ofReal(-y)));
861     }
862 
863     @Test
864     void testMultiplyRealNaN() {
865         final Complex x = Complex.ofCartesian(3.0, 4.0);
866         final double y = nan;
867         final Complex z = x.multiply(y);
868         Assertions.assertEquals(nan, z.getReal());
869         Assertions.assertEquals(nan, z.getImaginary());
870         // Equivalent
871         Assertions.assertEquals(z, x.multiply(ofReal(y)));
872     }
873 
874     @Test
875     void testMultiplyRealInf() {
876         final Complex x = Complex.ofCartesian(3.0, 4.0);
877         final double y = inf;
878         Complex z = x.multiply(y);
879         Assertions.assertEquals(inf, z.getReal());
880         Assertions.assertEquals(inf, z.getImaginary());
881         // Equivalent
882         Assertions.assertEquals(z, x.multiply(ofReal(y)));
883 
884         z = x.multiply(-y);
885         Assertions.assertEquals(-inf, z.getReal());
886         Assertions.assertEquals(-inf, z.getImaginary());
887         // Equivalent
888         Assertions.assertEquals(z, x.multiply(ofReal(-y)));
889     }
890 
891     @Test
892     void testMultiplyRealZero() {
893         final Complex x = Complex.ofCartesian(3.0, 4.0);
894         final double y = 0.0;
895         Complex z = x.multiply(y);
896         Assertions.assertEquals(0.0, z.getReal());
897         Assertions.assertEquals(0.0, z.getImaginary());
898         // Equivalent
899         Assertions.assertEquals(z, x.multiply(ofReal(y)));
900 
901         z = x.multiply(-y);
902         Assertions.assertEquals(-0.0, z.getReal());
903         Assertions.assertEquals(-0.0, z.getImaginary());
904         // Sign-preservation is a problem for imaginary: 0.0 - -0.0 == 0.0
905         final Complex z2 = x.multiply(ofReal(-y));
906         Assertions.assertEquals(-0.0, z2.getReal());
907         Assertions.assertEquals(0.0, z2.getImaginary(), "Expected no sign preservation");
908     }
909 
910     @Test
911     void testMultiplyImaginary() {
912         final Complex x = Complex.ofCartesian(3.0, 4.0);
913         final double y = 2.0;
914         Complex z = x.multiplyImaginary(y);
915         Assertions.assertEquals(-8.0, z.getReal());
916         Assertions.assertEquals(6.0, z.getImaginary());
917         // Equivalent
918         Assertions.assertEquals(z, x.multiply(ofImaginary(y)));
919 
920         z = x.multiplyImaginary(-y);
921         Assertions.assertEquals(8.0, z.getReal());
922         Assertions.assertEquals(-6.0, z.getImaginary());
923         // Equivalent
924         Assertions.assertEquals(z, x.multiply(ofImaginary(-y)));
925     }
926 
927     @Test
928     void testMultiplyImaginaryNaN() {
929         final Complex x = Complex.ofCartesian(3.0, 4.0);
930         final double y = nan;
931         final Complex z = x.multiplyImaginary(y);
932         Assertions.assertEquals(nan, z.getReal());
933         Assertions.assertEquals(nan, z.getImaginary());
934         // Equivalent
935         Assertions.assertEquals(z, x.multiply(ofImaginary(y)));
936     }
937 
938     @Test
939     void testMultiplyImaginaryInf() {
940         final Complex x = Complex.ofCartesian(3.0, 4.0);
941         final double y = inf;
942         Complex z = x.multiplyImaginary(y);
943         Assertions.assertEquals(-inf, z.getReal());
944         Assertions.assertEquals(inf, z.getImaginary());
945         // Equivalent
946         Assertions.assertEquals(z, x.multiply(ofImaginary(y)));
947 
948         z = x.multiplyImaginary(-y);
949         Assertions.assertEquals(inf, z.getReal());
950         Assertions.assertEquals(-inf, z.getImaginary());
951         // Equivalent
952         Assertions.assertEquals(z, x.multiply(ofImaginary(-y)));
953     }
954 
955     @Test
956     void testMultiplyImaginaryZero() {
957         final Complex x = Complex.ofCartesian(3.0, 4.0);
958         final double y = 0.0;
959         Complex z = x.multiplyImaginary(y);
960         Assertions.assertEquals(-0.0, z.getReal());
961         Assertions.assertEquals(0.0, z.getImaginary());
962         // Sign-preservation is a problem for real: 0.0 - -0.0 == 0.0
963         Complex z2 = x.multiply(ofImaginary(y));
964         Assertions.assertEquals(0.0, z2.getReal(), "Expected no sign preservation");
965         Assertions.assertEquals(0.0, z2.getImaginary());
966 
967         z = x.multiplyImaginary(-y);
968         Assertions.assertEquals(0.0, z.getReal());
969         Assertions.assertEquals(-0.0, z.getImaginary());
970         // Sign-preservation is a problem for imaginary: -0.0 - 0.0 == 0.0
971         z2 = x.multiply(ofImaginary(-y));
972         Assertions.assertEquals(0.0, z2.getReal());
973         Assertions.assertEquals(0.0, z2.getImaginary(), "Expected no sign preservation");
974     }
975 
976     @Test
977     void testNonZeroMultiplyI() {
978         final double[] parts = {3.0, 4.0};
979         for (final double a : parts) {
980             for (final double b : parts) {
981                 final Complex c = Complex.ofCartesian(a, b);
982                 final Complex x = c.multiplyImaginary(1.0);
983                 // Check verses algebra solution
984                 Assertions.assertEquals(-b, x.getReal());
985                 Assertions.assertEquals(a, x.getImaginary());
986                 final Complex z = c.multiply(Complex.I);
987                 Assertions.assertEquals(x, z);
988             }
989         }
990     }
991 
992     @Test
993     void testNonZeroMultiplyNegativeI() {
994         // This works no matter how you represent -I as a Complex
995         final double[] parts = {3.0, 4.0};
996         final Complex[] negIs = {Complex.ofCartesian(-0.0, -1), Complex.ofCartesian(0.0, -1)};
997         for (final double a : parts) {
998             for (final double b : parts) {
999                 final Complex c = Complex.ofCartesian(a, b);
1000                 final Complex x = c.multiplyImaginary(-1.0);
1001                 // Check verses algebra solution
1002                 Assertions.assertEquals(b, x.getReal());
1003                 Assertions.assertEquals(-a, x.getImaginary());
1004                 for (final Complex negI : negIs) {
1005                     final Complex z = c.multiply(negI);
1006                     Assertions.assertEquals(x, z);
1007                 }
1008             }
1009         }
1010     }
1011 
1012     @Test
1013     void testMultiplyZeroByI() {
1014         final double[] zeros = {-0.0, 0.0};
1015         for (final double a : zeros) {
1016             for (final double b : zeros) {
1017                 final Complex c = Complex.ofCartesian(a, b);
1018                 final Complex x = c.multiplyImaginary(1.0);
1019                 // Check verses algebra solution
1020                 Assertions.assertEquals(-b, x.getReal());
1021                 Assertions.assertEquals(a, x.getImaginary());
1022                 final Complex z = c.multiply(Complex.I);
1023                 // Does not work when imaginary part is +0.0.
1024                 if (Double.compare(b, 0.0) == 0) {
1025                     // (-0.0, 0.0).multiply( (0,1) ) => (-0.0, 0.0) expected (-0.0,-0.0)
1026                     // ( 0.0, 0.0).multiply( (0,1) ) => ( 0.0, 0.0) expected (-0.0, 0.0)
1027                     // Sign is allowed to be different for zero.
1028                     Assertions.assertEquals(0, z.getReal(), 0.0);
1029                     Assertions.assertEquals(0, z.getImaginary(), 0.0);
1030                     Assertions.assertNotEquals(x, z);
1031                 } else {
1032                     Assertions.assertEquals(x, z);
1033                 }
1034             }
1035         }
1036     }
1037 
1038     @Test
1039     void testMultiplyZeroByNegativeI() {
1040         // Depending on how we represent -I this does not work for 2/4 cases
1041         // but the cases are different. Here we test the negation of I.
1042         final Complex negI = Complex.I.negate();
1043         final double[] zeros = {-0.0, 0.0};
1044         for (final double a : zeros) {
1045             for (final double b : zeros) {
1046                 final Complex c = Complex.ofCartesian(a, b);
1047                 final Complex x = c.multiplyImaginary(-1.0);
1048                 // Check verses algebra solution
1049                 Assertions.assertEquals(b, x.getReal());
1050                 Assertions.assertEquals(-a, x.getImaginary());
1051                 final Complex z = c.multiply(negI);
1052                 final Complex z2 = c.multiply(Complex.I).negate();
1053                 // Does not work when imaginary part is -0.0.
1054                 if (Double.compare(b, -0.0) == 0) {
1055                     // (-0.0,-0.0).multiply( (-0.0,-1) ) => ( 0.0, 0.0) expected (-0.0, 0.0)
1056                     // ( 0.0,-0.0).multiply( (-0.0,-1) ) => (-0.0, 0.0) expected (-0.0,-0.0)
1057                     // Sign is allowed to be different for zero.
1058                     Assertions.assertEquals(0, z.getReal(), 0.0);
1059                     Assertions.assertEquals(0, z.getImaginary(), 0.0);
1060                     Assertions.assertNotEquals(x, z);
1061                     // When multiply by I.negate() fails multiply by I then negate()
1062                     // works!
1063                     Assertions.assertEquals(x, z2);
1064                 } else {
1065                     Assertions.assertEquals(x, z);
1066                     // When multiply by I.negate() works multiply by I then negate()
1067                     // fails!
1068                     Assertions.assertNotEquals(x, z2);
1069                 }
1070             }
1071         }
1072     }
1073 
1074     @Test
1075     void testDivide() {
1076         final Complex x = Complex.ofCartesian(3.0, 4.0);
1077         final Complex y = Complex.ofCartesian(5.0, 6.0);
1078         final Complex z = x.divide(y);
1079         Assertions.assertEquals(39.0 / 61.0, z.getReal());
1080         Assertions.assertEquals(2.0 / 61.0, z.getImaginary());
1081     }
1082 
1083     @Test
1084     void testDivideZero() {
1085         final Complex x = Complex.ofCartesian(3.0, 4.0);
1086         final Complex z = x.divide(Complex.ZERO);
1087         Assertions.assertEquals(INF, z);
1088     }
1089 
1090     @Test
1091     void testDivideZeroZero() {
1092         final Complex x = Complex.ofCartesian(0.0, 0.0);
1093         final Complex z = x.divide(Complex.ZERO);
1094         Assertions.assertEquals(NAN, z);
1095     }
1096 
1097     @Test
1098     void testDivideNaN() {
1099         final Complex x = Complex.ofCartesian(3.0, 4.0);
1100         final Complex z = x.divide(NAN);
1101         Assertions.assertTrue(z.isNaN());
1102     }
1103 
1104     @Test
1105     void testDivideNanInf() {
1106         Complex z = oneInf.divide(Complex.ONE);
1107         Assertions.assertTrue(Double.isNaN(z.getReal()));
1108         Assertions.assertEquals(inf, z.getImaginary());
1109 
1110         z = negInfNegInf.divide(oneNan);
1111         Assertions.assertTrue(Double.isNaN(z.getReal()));
1112         Assertions.assertTrue(Double.isNaN(z.getImaginary()));
1113 
1114         z = negInfInf.divide(Complex.ONE);
1115         Assertions.assertTrue(Double.isInfinite(z.getReal()));
1116         Assertions.assertTrue(Double.isInfinite(z.getImaginary()));
1117     }
1118 
1119     @Test
1120     void testDivideReal() {
1121         final Complex x = Complex.ofCartesian(3.0, 4.0);
1122         final double y = 2.0;
1123         Complex z = x.divide(y);
1124         Assertions.assertEquals(1.5, z.getReal());
1125         Assertions.assertEquals(2.0, z.getImaginary());
1126         // Equivalent
1127         Assertions.assertEquals(z, x.divide(ofReal(y)));
1128 
1129         z = x.divide(-y);
1130         Assertions.assertEquals(-1.5, z.getReal());
1131         Assertions.assertEquals(-2.0, z.getImaginary());
1132         // Equivalent
1133         Assertions.assertEquals(z, x.divide(ofReal(-y)));
1134     }
1135 
1136     @Test
1137     void testDivideRealNaN() {
1138         final Complex x = Complex.ofCartesian(3.0, 4.0);
1139         final double y = nan;
1140         final Complex z = x.divide(y);
1141         Assertions.assertEquals(nan, z.getReal());
1142         Assertions.assertEquals(nan, z.getImaginary());
1143         // Equivalent
1144         Assertions.assertEquals(z, x.divide(ofReal(y)));
1145     }
1146 
1147     @Test
1148     void testDivideRealInf() {
1149         final Complex x = Complex.ofCartesian(3.0, 4.0);
1150         final double y = inf;
1151         Complex z = x.divide(y);
1152         Assertions.assertEquals(0.0, z.getReal());
1153         Assertions.assertEquals(0.0, z.getImaginary());
1154         // Equivalent
1155         Assertions.assertEquals(z, x.divide(ofReal(y)));
1156 
1157         z = x.divide(-y);
1158         Assertions.assertEquals(-0.0, z.getReal());
1159         Assertions.assertEquals(-0.0, z.getImaginary());
1160         // Equivalent
1161         Assertions.assertEquals(z, x.divide(ofReal(-y)));
1162     }
1163 
1164     @Test
1165     void testDivideRealZero() {
1166         final Complex x = Complex.ofCartesian(3.0, 4.0);
1167         final double y = 0.0;
1168         Complex z = x.divide(y);
1169         Assertions.assertEquals(inf, z.getReal());
1170         Assertions.assertEquals(inf, z.getImaginary());
1171         // Equivalent
1172         Assertions.assertEquals(z, x.divide(ofReal(y)));
1173 
1174         z = x.divide(-y);
1175         Assertions.assertEquals(-inf, z.getReal());
1176         Assertions.assertEquals(-inf, z.getImaginary());
1177         // Equivalent
1178         Assertions.assertEquals(z, x.divide(ofReal(-y)));
1179     }
1180 
1181     @Test
1182     void testDivideImaginary() {
1183         final Complex x = Complex.ofCartesian(3.0, 4.0);
1184         final double y = 2.0;
1185         Complex z = x.divideImaginary(y);
1186         Assertions.assertEquals(2.0, z.getReal());
1187         Assertions.assertEquals(-1.5, z.getImaginary());
1188         // Equivalent
1189         Assertions.assertEquals(z, x.divide(ofImaginary(y)));
1190 
1191         z = x.divideImaginary(-y);
1192         Assertions.assertEquals(-2.0, z.getReal());
1193         Assertions.assertEquals(1.5, z.getImaginary());
1194         // Equivalent
1195         Assertions.assertEquals(z, x.divide(ofImaginary(-y)));
1196     }
1197 
1198     @Test
1199     void testDivideImaginaryNaN() {
1200         final Complex x = Complex.ofCartesian(3.0, 4.0);
1201         final double y = nan;
1202         final Complex z = x.divideImaginary(y);
1203         Assertions.assertEquals(nan, z.getReal());
1204         Assertions.assertEquals(nan, z.getImaginary());
1205         // Equivalent
1206         Assertions.assertEquals(z, x.divide(ofImaginary(y)));
1207     }
1208 
1209     @Test
1210     void testDivideImaginaryInf() {
1211         final Complex x = Complex.ofCartesian(3.0, 4.0);
1212         final double y = inf;
1213         Complex z = x.divideImaginary(y);
1214         Assertions.assertEquals(0.0, z.getReal());
1215         Assertions.assertEquals(-0.0, z.getImaginary());
1216         // Equivalent
1217         Assertions.assertEquals(z, x.divide(ofImaginary(y)));
1218 
1219         z = x.divideImaginary(-y);
1220         Assertions.assertEquals(-0.0, z.getReal());
1221         Assertions.assertEquals(0.0, z.getImaginary());
1222         // Equivalent
1223         Assertions.assertEquals(z, x.divide(ofImaginary(-y)));
1224     }
1225 
1226     @Test
1227     void testDivideImaginaryZero() {
1228         final Complex x = Complex.ofCartesian(3.0, 4.0);
1229         final double y = 0.0;
1230         Complex z = x.divideImaginary(y);
1231         Assertions.assertEquals(inf, z.getReal());
1232         Assertions.assertEquals(-inf, z.getImaginary());
1233         // Sign-preservation is a problem for imaginary: 0.0 - -0.0 == 0.0
1234         Complex z2 = x.divide(ofImaginary(y));
1235         Assertions.assertEquals(inf, z2.getReal());
1236         Assertions.assertEquals(inf, z2.getImaginary(), "Expected no sign preservation");
1237 
1238         z = x.divideImaginary(-y);
1239         Assertions.assertEquals(-inf, z.getReal());
1240         Assertions.assertEquals(inf, z.getImaginary());
1241         // Sign-preservation is a problem for real: 0.0 + -0.0 == 0.0
1242         z2 = x.divide(ofImaginary(-y));
1243         Assertions.assertEquals(inf, z2.getReal(), "Expected no sign preservation");
1244         Assertions.assertEquals(inf, z2.getImaginary());
1245     }
1246 
1247     /**
1248      * Arithmetic test using combinations of +/- x for real, imaginary and the double
1249      * argument for add, subtract, subtractFrom, multiply and divide, where x is zero or
1250      * non-zero.
1251      *
1252      * <p>The differences to the same argument as a Complex are tested. The only
1253      * differences should be the sign of zero in certain cases.
1254      */
1255     @Test
1256     void testSignedArithmetic() {
1257         // The following lists the conditions for the double primitive operation where
1258         // the Complex operation is different. Here the double argument can be:
1259         // x : any value
1260         // +x : positive
1261         // +0.0: positive zero
1262         // -x : negative
1263         // -0.0: negative zero
1264         // 0 : any zero
1265         // use y for any non-zero value
1266 
1267         // Check the known fail cases using an integer as a bit set.
1268         // If a bit is 1 then the case is known to fail.
1269         // The 64 cases are enumerated as:
1270         // 4 cases: (a,-0.0) operation on -0.0, 0.0, -2, 3
1271         // 4 cases: (a, 0.0) operation on -0.0, 0.0, -2, 3
1272         // 4 cases: (a,-2.0) operation on -0.0, 0.0, -2, 3
1273         // 4 cases: (a, 3.0) operation on -0.0, 0.0, -2, 3
1274         // with a in [-0.0, 0.0, -2, 3]
1275         // The least significant bit is for the first case.
1276 
1277         // The bit set was generated for this test. The summary below demonstrates
1278         // documenting the sign change cases for multiply and divide is non-trivial
1279         // and the javadoc in Complex does not break down the actual cases.
1280 
1281         // 16: (x,-0.0) + x
1282         assertSignedZeroArithmetic("addReal", Complex::add, ComplexTest::ofReal, Complex::add,
1283             0b1111000000000000111100000000000011110000000000001111L);
1284         // 16: (-0.0,x) + x
1285         assertSignedZeroArithmetic("addImaginary", Complex::addImaginary, ComplexTest::ofImaginary, Complex::add,
1286             0b1111111111111111L);
1287         // 0:
1288         assertSignedZeroArithmetic("subtractReal", Complex::subtract, ComplexTest::ofReal, Complex::subtract, 0);
1289         // 0:
1290         assertSignedZeroArithmetic("subtractImaginary", Complex::subtractImaginary, ComplexTest::ofImaginary,
1291             Complex::subtract, 0);
1292         // 16: x - (x,+0.0)
1293         assertSignedZeroArithmetic("subtractFromReal", Complex::subtractFrom, ComplexTest::ofReal,
1294             (y, z) -> z.subtract(y), 0b11110000000000001111000000000000111100000000000011110000L);
1295         // 16: x - (+0.0,x)
1296         assertSignedZeroArithmetic("subtractFromImaginary", Complex::subtractFromImaginary, ComplexTest::ofImaginary,
1297             (y, z) -> z.subtract(y), 0b11111111111111110000000000000000L);
1298         // 4: (-0.0,-x) * +x
1299         // 4: (+0.0,-0.0) * x
1300         // 4: (+0.0,x) * -x
1301         // 2: (-y,-x) * +0.0
1302         // 2: (+y,+0.0) * -x
1303         // 2: (+y,-0.0) * +x
1304         // 2: (+y,-x) * -0.0
1305         // 2: (+x,-y) * +0.0
1306         // 2: (+x,+y) * -0.0
1307         assertSignedZeroArithmetic("multiplyReal", Complex::multiply, ComplexTest::ofReal, Complex::multiply,
1308             0b1001101011011000000100000001000010111010111110000101000001010L);
1309         // 4: (-0.0,+x) * +x
1310         // 2: (+0.0,-0.0) * -x
1311         // 4: (+0.0,+0.0) * x
1312         // 2: (+0.0,+y) * -x
1313         // 2: (-y,+x) * +0.0
1314         // 4: (+y,x) * -0.0
1315         // 2: (+0.0,+/-y) * -/+0
1316         // 2: (+y,+/-0.0) * +/-y (sign 0.0 matches sign y)
1317         // 2: (+y,+x) * +0.0
1318         assertSignedZeroArithmetic("multiplyImaginary", Complex::multiplyImaginary, ComplexTest::ofImaginary,
1319             Complex::multiply, 0b11000110110101001000000010000001110001111101011010000010100000L);
1320         // 2: (-0.0,0) / +y
1321         // 2: (+0.0,+x) / -y
1322         // 2: (-x,0) / -y
1323         // 1: (-0.0,+y) / +y
1324         // 1: (-y,+0.0) / -y
1325         assertSignedZeroArithmetic("divideReal", Complex::divide, ComplexTest::ofReal, Complex::divide,
1326             0b100100001000000010000001000000011001000L);
1327 
1328         // DivideImaginary has its own test as the result is not always equal ignoring the
1329         // sign.
1330     }
1331 
1332     private static void assertSignedZeroArithmetic(String name, BiFunction<Complex, Double, Complex> doubleOperation,
1333         DoubleFunction<Complex> doubleToComplex, BiFunction<Complex, Complex, Complex> complexOperation,
1334         long expectedFailures) {
1335         // With an operation on zero or non-zero arguments
1336         final double[] arguments = {-0.0, 0.0, -2, 3};
1337         for (final double a : arguments) {
1338             for (final double b : arguments) {
1339                 final Complex c = Complex.ofCartesian(a, b);
1340                 for (final double arg : arguments) {
1341                     final Complex y = doubleOperation.apply(c, arg);
1342                     final Complex z = complexOperation.apply(c, doubleToComplex.apply(arg));
1343                     final boolean expectedFailure = (expectedFailures & 0x1) == 1;
1344                     expectedFailures >>>= 1;
1345                     // Check the same answer. Sign is allowed to be different for zero.
1346                     Assertions.assertEquals(y.getReal(), z.getReal(), 0, () -> c + " " + name + " " + arg + ": real");
1347                     Assertions.assertEquals(y.getImaginary(), z.getImaginary(), 0,
1348                         () -> c + " " + name + " " + arg + ": imaginary");
1349                     Assertions.assertEquals(expectedFailure, !y.equals(z),
1350                         () -> c + " " + name + " " + arg + ": sign-difference");
1351                 }
1352             }
1353         }
1354     }
1355 
1356     /**
1357      * Arithmetic test using combinations of +/- x for real, imaginary and and the double
1358      * argument for divideImaginary, where x is zero or non-zero.
1359      *
1360      * <p>The differences to the same argument as a Complex are tested. This checks for
1361      * sign differences of zero or, if divide by zero, that the result is equal to divide
1362      * by zero using a Complex then multiplied by I.
1363      */
1364     @Test
1365     void testSignedDivideImaginaryArithmetic() {
1366         // Cases for divide by non-zero:
1367         // 2: (-0.0,+x) / -y
1368         // 4: (+x,+/-0.0) / -/+y
1369         // 2: (+0.0,+x) / +y
1370         // Cases for divide by zero after multiplication of the Complex result by I:
1371         // 2: (-0.0,+/-y) / +0.0
1372         // 2: (+0.0,+/-y) / +0.0
1373         // 4: (-y,x) / +0.0
1374         // 4: (y,x) / +0.0
1375         // If multiplied by -I all the divide by -0.0 cases have sign errors and / +0.0 is
1376         // OK.
1377         long expectedFailures = 0b11001101111011001100110011001110110011110010000111001101000000L;
1378         // With an operation on zero or non-zero arguments
1379         final double[] arguments = {-0.0, 0.0, -2, 3};
1380         for (final double a : arguments) {
1381             for (final double b : arguments) {
1382                 final Complex c = Complex.ofCartesian(a, b);
1383                 for (final double arg : arguments) {
1384                     final Complex y = c.divideImaginary(arg);
1385                     Complex z = c.divide(ofImaginary(arg));
1386                     final boolean expectedFailure = (expectedFailures & 0x1) == 1;
1387                     expectedFailures >>>= 1;
1388                     // If divide by zero then the divide(Complex) method matches divide by real.
1389                     // To match divide by imaginary requires multiplication by I.
1390                     if (arg == 0) {
1391                         // Same result if multiplied by I. The sign may not match so
1392                         // optionally ignore the sign of the infinity.
1393                         z = z.multiplyImaginary(1);
1394                         final double ya = expectedFailure ? Math.abs(y.getReal()) : y.getReal();
1395                         final double yb = expectedFailure ? Math.abs(y.getImaginary()) : y.getImaginary();
1396                         final double za = expectedFailure ? Math.abs(z.getReal()) : z.getReal();
1397                         final double zb = expectedFailure ? Math.abs(z.getImaginary()) : z.getImaginary();
1398                         Assertions.assertEquals(ya, za, () -> c + " divideImaginary " + arg + ": real");
1399                         Assertions.assertEquals(yb, zb, () -> c + " divideImaginary " + arg + ": imaginary");
1400                     } else {
1401                         // Check the same answer. Sign is allowed to be different for zero.
1402                         Assertions.assertEquals(y.getReal(), z.getReal(), 0,
1403                             () -> c + " divideImaginary " + arg + ": real");
1404                         Assertions.assertEquals(y.getImaginary(), z.getImaginary(), 0,
1405                             () -> c + " divideImaginary " + arg + ": imaginary");
1406                         Assertions.assertEquals(expectedFailure, !y.equals(z),
1407                             () -> c + " divideImaginary " + arg + ": sign-difference");
1408                     }
1409                 }
1410             }
1411         }
1412     }
1413 
1414     @Test
1415     void testLog10() {
1416         final double ln10 = Math.log(10);
1417         final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
1418         for (int i = 0; i < 10; i++) {
1419             final Complex z = Complex.ofCartesian(rng.nextDouble() * 2, rng.nextDouble() * 2);
1420             final Complex lnz = z.log();
1421             final Complex log10z = z.log10();
1422             // This is prone to floating-point error so use a delta
1423             Assertions.assertEquals(lnz.getReal() / ln10, log10z.getReal(), 1e-12, "real");
1424             // This test should be exact
1425             Assertions.assertEquals(lnz.getImaginary(), log10z.getImaginary(), "imag");
1426         }
1427     }
1428 
1429     @Test
1430     void testPow() {
1431         final Complex x = Complex.ofCartesian(3, 4);
1432         final double yDouble = 5.0;
1433         final Complex yComplex = ofReal(yDouble);
1434         Assertions.assertEquals(x.pow(yComplex), x.pow(yDouble));
1435     }
1436 
1437     @Test
1438     void testPowComplexRealZero() {
1439         // Hits the edge case when real == 0 but imaginary != 0
1440         final Complex x = Complex.ofCartesian(0, 1);
1441         final Complex z = Complex.ofCartesian(2, 3);
1442         final Complex c = x.pow(z);
1443         // Answer from g++
1444         Assertions.assertEquals(-0.008983291021129429, c.getReal());
1445         Assertions.assertEquals(1.1001358594835313e-18, c.getImaginary());
1446     }
1447 
1448     @Test
1449     void testPowComplexZeroBase() {
1450         final double x = Double.MIN_VALUE;
1451         assertPowComplexZeroBase(0, 0, NAN);
1452         assertPowComplexZeroBase(0, x, NAN);
1453         assertPowComplexZeroBase(x, x, NAN);
1454         assertPowComplexZeroBase(x, 0, Complex.ZERO);
1455     }
1456 
1457     private static void assertPowComplexZeroBase(double re, double im, Complex expected) {
1458         final Complex z = Complex.ofCartesian(re, im);
1459         final Complex c = Complex.ZERO.pow(z);
1460         Assertions.assertEquals(expected, c);
1461     }
1462 
1463     @Test
1464     void testPowScalerRealZero() {
1465         // Hits the edge case when real == 0 but imaginary != 0
1466         final Complex x = Complex.ofCartesian(0, 1);
1467         final Complex c = x.pow(2);
1468         // Answer from g++
1469         Assertions.assertEquals(-1, c.getReal());
1470         Assertions.assertEquals(1.2246467991473532e-16, c.getImaginary());
1471     }
1472 
1473     @Test
1474     void testPowScalarZeroBase() {
1475         final double x = Double.MIN_VALUE;
1476         assertPowScalarZeroBase(0, NAN);
1477         assertPowScalarZeroBase(x, Complex.ZERO);
1478     }
1479 
1480     private static void assertPowScalarZeroBase(double exp, Complex expected) {
1481         final Complex c = Complex.ZERO.pow(exp);
1482         Assertions.assertEquals(expected, c);
1483     }
1484 
1485     @Test
1486     void testPowNanBase() {
1487         final Complex x = NAN;
1488         final double yDouble = 5.0;
1489         final Complex yComplex = ofReal(yDouble);
1490         Assertions.assertEquals(x.pow(yComplex), x.pow(yDouble));
1491     }
1492 
1493     @Test
1494     void testPowNanExponent() {
1495         final Complex x = Complex.ofCartesian(3, 4);
1496         final double yDouble = Double.NaN;
1497         final Complex yComplex = ofReal(yDouble);
1498         Assertions.assertEquals(x.pow(yComplex), x.pow(yDouble));
1499     }
1500 
1501     @Test
1502     void testSqrtPolar() {
1503         final double tol = 1e-12;
1504         double r = 1;
1505         for (int i = 0; i < 5; i++) {
1506             r += i;
1507             double theta = 0;
1508             for (int j = 0; j < 11; j++) {
1509                 theta += pi / 12;
1510                 final Complex z = Complex.ofPolar(r, theta);
1511                 final Complex sqrtz = Complex.ofPolar(Math.sqrt(r), theta / 2);
1512                 TestUtils.assertEquals(sqrtz, z.sqrt(), tol);
1513             }
1514         }
1515     }
1516 
1517     @Test
1518     void testZerothRootThrows() {
1519         final Complex c = Complex.ofCartesian(1, 1);
1520         Assertions.assertThrows(IllegalArgumentException.class, () -> c.nthRoot(0),
1521             "zeroth root should not be allowed");
1522     }
1523 
1524     /**
1525      * Test: computing <b>third roots</b> of z.
1526      *
1527      * <pre>
1528      * <code>
1529      * <b>z = -2 + 2 * i</b>
1530      *   => z_0 =  1      +          i
1531      *   => z_1 = -1.3660 + 0.3660 * i
1532      *   => z_2 =  0.3660 - 1.3660 * i
1533      * </code>
1534      * </pre>
1535      */
1536     @Test
1537     void testNthRootNormalThirdRoot() {
1538         // The complex number we want to compute all third-roots for.
1539         final Complex z = Complex.ofCartesian(-2, 2);
1540         // The List holding all third roots
1541         final Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]);
1542         // Returned Collection must not be empty!
1543         Assertions.assertEquals(3, thirdRootsOfZ.length);
1544         // test z_0
1545         Assertions.assertEquals(1.0, thirdRootsOfZ[0].getReal(), 1.0e-5);
1546         Assertions.assertEquals(1.0, thirdRootsOfZ[0].getImaginary(), 1.0e-5);
1547         // test z_1
1548         Assertions.assertEquals(-1.3660254037844386, thirdRootsOfZ[1].getReal(), 1.0e-5);
1549         Assertions.assertEquals(0.36602540378443843, thirdRootsOfZ[1].getImaginary(), 1.0e-5);
1550         // test z_2
1551         Assertions.assertEquals(0.366025403784439, thirdRootsOfZ[2].getReal(), 1.0e-5);
1552         Assertions.assertEquals(-1.3660254037844384, thirdRootsOfZ[2].getImaginary(), 1.0e-5);
1553     }
1554 
1555     /**
1556      * Test: computing <b>fourth roots</b> of z.
1557      *
1558      * <pre>
1559      * <code>
1560      * <b>z = 5 - 2 * i</b>
1561      *   => z_0 =  1.5164 - 0.1446 * i
1562      *   => z_1 =  0.1446 + 1.5164 * i
1563      *   => z_2 = -1.5164 + 0.1446 * i
1564      *   => z_3 = -1.5164 - 0.1446 * i
1565      * </code>
1566      * </pre>
1567      */
1568     @Test
1569     void testNthRootNormalFourthRoot() {
1570         // The complex number we want to compute all third-roots for.
1571         final Complex z = Complex.ofCartesian(5, -2);
1572         // The List holding all fourth roots
1573         final Complex[] fourthRootsOfZ = z.nthRoot(4).toArray(new Complex[0]);
1574         // Returned Collection must not be empty!
1575         Assertions.assertEquals(4, fourthRootsOfZ.length);
1576         // test z_0
1577         Assertions.assertEquals(1.5164629308487783, fourthRootsOfZ[0].getReal(), 1.0e-5);
1578         Assertions.assertEquals(-0.14469266210702247, fourthRootsOfZ[0].getImaginary(), 1.0e-5);
1579         // test z_1
1580         Assertions.assertEquals(0.14469266210702256, fourthRootsOfZ[1].getReal(), 1.0e-5);
1581         Assertions.assertEquals(1.5164629308487783, fourthRootsOfZ[1].getImaginary(), 1.0e-5);
1582         // test z_2
1583         Assertions.assertEquals(-1.5164629308487783, fourthRootsOfZ[2].getReal(), 1.0e-5);
1584         Assertions.assertEquals(0.14469266210702267, fourthRootsOfZ[2].getImaginary(), 1.0e-5);
1585         // test z_3
1586         Assertions.assertEquals(-0.14469266210702275, fourthRootsOfZ[3].getReal(), 1.0e-5);
1587         Assertions.assertEquals(-1.5164629308487783, fourthRootsOfZ[3].getImaginary(), 1.0e-5);
1588     }
1589 
1590     /**
1591      * Test: computing <b>third roots</b> of z.
1592      *
1593      * <pre>
1594      * <code>
1595      * <b>z = 8</b>
1596      *   => z_0 =  2
1597      *   => z_1 = -1 + 1.73205 * i
1598      *   => z_2 = -1 - 1.73205 * i
1599      * </code>
1600      * </pre>
1601      */
1602     @Test
1603     void testNthRootCornercaseThirdRootImaginaryPartEmpty() {
1604         // The number 8 has three third roots. One we all already know is the number 2.
1605         // But there are two more complex roots.
1606         final Complex z = Complex.ofCartesian(8, 0);
1607         // The List holding all third roots
1608         final Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]);
1609         // Returned Collection must not be empty!
1610         Assertions.assertEquals(3, thirdRootsOfZ.length);
1611         // test z_0
1612         Assertions.assertEquals(2.0, thirdRootsOfZ[0].getReal(), 1.0e-5);
1613         Assertions.assertEquals(0.0, thirdRootsOfZ[0].getImaginary(), 1.0e-5);
1614         // test z_1
1615         Assertions.assertEquals(-1.0, thirdRootsOfZ[1].getReal(), 1.0e-5);
1616         Assertions.assertEquals(1.7320508075688774, thirdRootsOfZ[1].getImaginary(), 1.0e-5);
1617         // test z_2
1618         Assertions.assertEquals(-1.0, thirdRootsOfZ[2].getReal(), 1.0e-5);
1619         Assertions.assertEquals(-1.732050807568877, thirdRootsOfZ[2].getImaginary(), 1.0e-5);
1620     }
1621 
1622     /**
1623      * Test: computing <b>third roots</b> of z with real part 0.
1624      *
1625      * <pre>
1626      * <code>
1627      * <b>z = 2 * i</b>
1628      *   => z_0 =  1.0911 + 0.6299 * i
1629      *   => z_1 = -1.0911 + 0.6299 * i
1630      *   => z_2 = -2.3144 - 1.2599 * i
1631      * </code>
1632      * </pre>
1633      */
1634     @Test
1635     void testNthRootCornercaseThirdRootRealPartZero() {
1636         // complex number with only imaginary part
1637         final Complex z = Complex.ofCartesian(0, 2);
1638         // The List holding all third roots
1639         final Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]);
1640         // Returned Collection must not be empty!
1641         Assertions.assertEquals(3, thirdRootsOfZ.length);
1642         // test z_0
1643         Assertions.assertEquals(1.0911236359717216, thirdRootsOfZ[0].getReal(), 1.0e-5);
1644         Assertions.assertEquals(0.6299605249474365, thirdRootsOfZ[0].getImaginary(), 1.0e-5);
1645         // test z_1
1646         Assertions.assertEquals(-1.0911236359717216, thirdRootsOfZ[1].getReal(), 1.0e-5);
1647         Assertions.assertEquals(0.6299605249474365, thirdRootsOfZ[1].getImaginary(), 1.0e-5);
1648         // test z_2
1649         Assertions.assertEquals(-2.3144374213981936E-16, thirdRootsOfZ[2].getReal(), 1.0e-5);
1650         Assertions.assertEquals(-1.2599210498948732, thirdRootsOfZ[2].getImaginary(), 1.0e-5);
1651     }
1652 
1653     /**
1654      * Test: compute <b>third roots</b> using a negative argument to go clockwise around
1655      * the unit circle. Fourth roots of one are taken in both directions around the circle
1656      * using positive and negative arguments.
1657      *
1658      * <pre>
1659      * <code>
1660      * <b>z = 1</b>
1661      *   => z_0 = Positive: 1,0 ; Negative: 1,0
1662      *   => z_1 = Positive: 0,1 ; Negative: 0,-1
1663      *   => z_2 = Positive: -1,0 ; Negative: -1,0
1664      *   => z_3 = Positive: 0,-1 ; Negative: 0,1
1665      * </code>
1666      * </pre>
1667      */
1668     @Test
1669     void testNthRootNegativeArg() {
1670         // The complex number we want to compute all third-roots for.
1671         final Complex z = Complex.ofCartesian(1, 0);
1672         // The List holding all fourth roots
1673         Complex[] fourthRootsOfZ = z.nthRoot(4).toArray(new Complex[0]);
1674         // test z_0
1675         Assertions.assertEquals(1, fourthRootsOfZ[0].getReal(), 1.0e-5);
1676         Assertions.assertEquals(0, fourthRootsOfZ[0].getImaginary(), 1.0e-5);
1677         // test z_1
1678         Assertions.assertEquals(0, fourthRootsOfZ[1].getReal(), 1.0e-5);
1679         Assertions.assertEquals(1, fourthRootsOfZ[1].getImaginary(), 1.0e-5);
1680         // test z_2
1681         Assertions.assertEquals(-1, fourthRootsOfZ[2].getReal(), 1.0e-5);
1682         Assertions.assertEquals(0, fourthRootsOfZ[2].getImaginary(), 1.0e-5);
1683         // test z_3
1684         Assertions.assertEquals(0, fourthRootsOfZ[3].getReal(), 1.0e-5);
1685         Assertions.assertEquals(-1, fourthRootsOfZ[3].getImaginary(), 1.0e-5);
1686         // go clockwise around the unit circle using negative argument
1687         fourthRootsOfZ = z.nthRoot(-4).toArray(new Complex[0]);
1688         // test z_0
1689         Assertions.assertEquals(1, fourthRootsOfZ[0].getReal(), 1.0e-5);
1690         Assertions.assertEquals(0, fourthRootsOfZ[0].getImaginary(), 1.0e-5);
1691         // test z_1
1692         Assertions.assertEquals(0, fourthRootsOfZ[1].getReal(), 1.0e-5);
1693         Assertions.assertEquals(-1, fourthRootsOfZ[1].getImaginary(), 1.0e-5);
1694         // test z_2
1695         Assertions.assertEquals(-1, fourthRootsOfZ[2].getReal(), 1.0e-5);
1696         Assertions.assertEquals(0, fourthRootsOfZ[2].getImaginary(), 1.0e-5);
1697         // test z_3
1698         Assertions.assertEquals(0, fourthRootsOfZ[3].getReal(), 1.0e-5);
1699         Assertions.assertEquals(1, fourthRootsOfZ[3].getImaginary(), 1.0e-5);
1700     }
1701 
1702     @Test
1703     void testNthRootNan() {
1704         final int n = 3;
1705         final Complex z = ofReal(Double.NaN);
1706         final List<Complex> r = z.nthRoot(n);
1707         Assertions.assertEquals(n, r.size());
1708         for (final Complex c : r) {
1709             Assertions.assertTrue(Double.isNaN(c.getReal()));
1710             Assertions.assertTrue(Double.isNaN(c.getImaginary()));
1711         }
1712     }
1713 
1714     @Test
1715     void testNthRootInf() {
1716         final int n = 3;
1717         final Complex z = ofReal(Double.NEGATIVE_INFINITY);
1718         final List<Complex> r = z.nthRoot(n);
1719         Assertions.assertEquals(n, r.size());
1720     }
1721 
1722     @Test
1723     void testEqualsWithNull() {
1724         final Complex x = Complex.ofCartesian(3.0, 4.0);
1725         Assertions.assertNotEquals(x, null);
1726     }
1727 
1728     @Test
1729     void testEqualsWithAnotherClass() {
1730         final Complex x = Complex.ofCartesian(3.0, 4.0);
1731         Assertions.assertNotEquals(x, new Object());
1732     }
1733 
1734     @Test
1735     void testEqualsWithSameObject() {
1736         final Complex x = Complex.ofCartesian(3.0, 4.0);
1737         Assertions.assertEquals(x, x);
1738     }
1739 
1740     @Test
1741     void testEqualsWithCopyObject() {
1742         final Complex x = Complex.ofCartesian(3.0, 4.0);
1743         final Complex y = Complex.ofCartesian(3.0, 4.0);
1744         Assertions.assertEquals(x, y);
1745     }
1746 
1747     @Test
1748     void testEqualsWithRealDifference() {
1749         final Complex x = Complex.ofCartesian(0.0, 0.0);
1750         final Complex y = Complex.ofCartesian(0.0 + Double.MIN_VALUE, 0.0);
1751         Assertions.assertNotEquals(x, y);
1752     }
1753 
1754     @Test
1755     void testEqualsWithImaginaryDifference() {
1756         final Complex x = Complex.ofCartesian(0.0, 0.0);
1757         final Complex y = Complex.ofCartesian(0.0, 0.0 + Double.MIN_VALUE);
1758         Assertions.assertNotEquals(x, y);
1759     }
1760 
1761     /**
1762      * Test {@link Complex#equals(Object)}. It should be consistent with
1763      * {@link Arrays#equals(double[], double[])} called using the components of two
1764      * complex numbers.
1765      */
1766     @Test
1767     void testEqualsIsConsistentWithArraysEquals() {
1768         // Explicit check of the cases documented in the Javadoc:
1769         assertEqualsIsConsistentWithArraysEquals(Complex.ofCartesian(Double.NaN, 0.0),
1770             Complex.ofCartesian(Double.NaN, 1.0), "NaN real and different non-NaN imaginary");
1771         assertEqualsIsConsistentWithArraysEquals(Complex.ofCartesian(0.0, Double.NaN),
1772             Complex.ofCartesian(1.0, Double.NaN), "Different non-NaN real and NaN imaginary");
1773         assertEqualsIsConsistentWithArraysEquals(Complex.ofCartesian(0.0, 0.0), Complex.ofCartesian(-0.0, 0.0),
1774             "Different real zeros");
1775         assertEqualsIsConsistentWithArraysEquals(Complex.ofCartesian(0.0, 0.0), Complex.ofCartesian(0.0, -0.0),
1776             "Different imaginary zeros");
1777 
1778         // Test some values of edge cases
1779         final double[] values = {Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, -1, 0, 1};
1780         final ArrayList<Complex> list = createCombinations(values);
1781 
1782         for (final Complex c : list) {
1783             final double real = c.getReal();
1784             final double imag = c.getImaginary();
1785 
1786             // Check a copy is equal
1787             assertEqualsIsConsistentWithArraysEquals(c, Complex.ofCartesian(real, imag), "Copy complex");
1788 
1789             // Perform the smallest change to the two components
1790             final double realDelta = smallestChange(real);
1791             final double imagDelta = smallestChange(imag);
1792             Assertions.assertNotEquals(real, realDelta, "Real was not changed");
1793             Assertions.assertNotEquals(imag, imagDelta, "Imaginary was not changed");
1794 
1795             assertEqualsIsConsistentWithArraysEquals(c, Complex.ofCartesian(realDelta, imag), "Delta real");
1796             assertEqualsIsConsistentWithArraysEquals(c, Complex.ofCartesian(real, imagDelta), "Delta imaginary");
1797         }
1798     }
1799 
1800     /**
1801      * Specific test to target different representations that return {@code true} for
1802      * {@link Complex#isNaN()} are {@code false} for {@link Complex#equals(Object)}.
1803      */
1804     @Test
1805     void testEqualsWithDifferentNaNs() {
1806         // Test some NaN combinations
1807         final double[] values = {Double.NaN, 0, 1};
1808         final ArrayList<Complex> list = createCombinations(values);
1809 
1810         // Is the all-vs-all comparison only the exact same values should be equal, e.g.
1811         // (nan,0) not equals (nan,nan)
1812         // (nan,0) equals (nan,0)
1813         // (nan,0) not equals (0,nan)
1814         for (int i = 0; i < list.size(); i++) {
1815             final Complex c1 = list.get(i);
1816             final Complex copy = Complex.ofCartesian(c1.getReal(), c1.getImaginary());
1817             assertEqualsIsConsistentWithArraysEquals(c1, copy, "Copy is not equal");
1818             for (int j = i + 1; j < list.size(); j++) {
1819                 final Complex c2 = list.get(j);
1820                 assertEqualsIsConsistentWithArraysEquals(c1, c2, "Different NaNs should not be equal");
1821             }
1822         }
1823     }
1824 
1825     /**
1826      * Test the two complex numbers with {@link Complex#equals(Object)} and check the
1827      * result is consistent with {@link Arrays#equals(double[], double[])}.
1828      *
1829      * @param c1 the first complex
1830      * @param c2 the second complex
1831      * @param msg the message to append to an assertion error
1832      */
1833     private static void assertEqualsIsConsistentWithArraysEquals(Complex c1, Complex c2, String msg) {
1834         final boolean expected = Arrays.equals(new double[] {c1.getReal(), c1.getImaginary()},
1835             new double[] {c2.getReal(), c2.getImaginary()});
1836         final boolean actual = c1.equals(c2);
1837         Assertions.assertEquals(expected, actual,
1838             () -> String.format("equals(Object) is not consistent with Arrays.equals: %s. %s vs %s", msg, c1, c2));
1839     }
1840 
1841     /**
1842      * Test {@link Complex#hashCode()}. It should be consistent with
1843      * {@link Arrays#hashCode(double[])} called using the components of the complex number
1844      * and fulfil the contract of {@link Object#hashCode()}, i.e. objects with different
1845      * hash codes are {@code false} for {@link Object#equals(Object)}.
1846      */
1847     @Test
1848     void testHashCode() {
1849         // Test some values match Arrays.hashCode(double[])
1850         final double[] values = {Double.NaN, Double.NEGATIVE_INFINITY, -3.45, -1, -0.0, 0.0, Double.MIN_VALUE, 1, 3.45,
1851             Double.POSITIVE_INFINITY};
1852         final ArrayList<Complex> list = createCombinations(values);
1853 
1854         final String msg = "'equals' not compatible with 'hashCode'";
1855 
1856         for (final Complex c : list) {
1857             final double real = c.getReal();
1858             final double imag = c.getImaginary();
1859             final int expected = Arrays.hashCode(new double[] {real, imag});
1860             final int hash = c.hashCode();
1861             Assertions.assertEquals(expected, hash, "hashCode does not match Arrays.hashCode({re, im})");
1862 
1863             // Test a copy has the same hash code, i.e. is not
1864             // System.identityHashCode(Object)
1865             final Complex copy = Complex.ofCartesian(real, imag);
1866             Assertions.assertEquals(hash, copy.hashCode(), "Copy hash code is not equal");
1867 
1868             // MATH-1118
1869             // "equals" and "hashCode" must be compatible: if two objects have
1870             // different hash codes, "equals" must return false.
1871             // Perform the smallest change to the two components.
1872             // Note: The hash could actually be the same so we check it changes.
1873             final double realDelta = smallestChange(real);
1874             final double imagDelta = smallestChange(imag);
1875             Assertions.assertNotEquals(real, realDelta, "Real was not changed");
1876             Assertions.assertNotEquals(imag, imagDelta, "Imaginary was not changed");
1877 
1878             final Complex cRealDelta = Complex.ofCartesian(realDelta, imag);
1879             final Complex cImagDelta = Complex.ofCartesian(real, imagDelta);
1880             if (hash != cRealDelta.hashCode()) {
1881                 Assertions.assertNotEquals(c, cRealDelta, () -> "real+delta: " + msg);
1882             }
1883             if (hash != cImagDelta.hashCode()) {
1884                 Assertions.assertNotEquals(c, cImagDelta, () -> "imaginary+delta: " + msg);
1885             }
1886         }
1887     }
1888 
1889     /**
1890      * Specific test that different representations of zero satisfy the contract of
1891      * {@link Object#hashCode()}: if two objects have different hash codes, "equals" must
1892      * return false. This is an issue with using {@link Double#hashCode(double)} to create
1893      * hash codes and {@code ==} for equality when using different representations of
1894      * zero: Double.hashCode(-0.0) != Double.hashCode(0.0) but -0.0 == 0.0 is
1895      * {@code true}.
1896      *
1897      * @see <a
1898      * href="https://issues.apache.org/jira/projects/MATH/issues/MATH-1118">MATH-1118</a>
1899      */
1900     @Test
1901     void testHashCodeWithDifferentZeros() {
1902         final double[] values = {-0.0, 0.0};
1903         final ArrayList<Complex> list = createCombinations(values);
1904 
1905         // Explicit test for issue MATH-1118
1906         // "equals" and "hashCode" must be compatible
1907         for (int i = 0; i < list.size(); i++) {
1908             final Complex c1 = list.get(i);
1909             for (int j = i + 1; j < list.size(); j++) {
1910                 final Complex c2 = list.get(j);
1911                 if (c1.hashCode() != c2.hashCode()) {
1912                     Assertions.assertNotEquals(c1, c2, "'equals' not compatible with 'hashCode'");
1913                 }
1914             }
1915         }
1916     }
1917 
1918     /**
1919      * Creates a list of Complex numbers using an all-vs-all combination of the provided
1920      * values for both the real and imaginary parts.
1921      *
1922      * @param values the values
1923      * @return the list
1924      */
1925     private static ArrayList<Complex> createCombinations(final double[] values) {
1926         final ArrayList<Complex> list = new ArrayList<>(values.length * values.length);
1927         for (final double re : values) {
1928             for (final double im : values) {
1929                 list.add(Complex.ofCartesian(re, im));
1930             }
1931         }
1932         return list;
1933     }
1934 
1935     /**
1936      * Perform the smallest change to the value. This returns the next double value
1937      * adjacent to d in the direction of infinity. Edge cases: if already infinity then
1938      * return the next closest in the direction of negative infinity; if nan then return
1939      * 0.
1940      *
1941      * @param x the x
1942      * @return the new value
1943      */
1944     private static double smallestChange(double x) {
1945         if (Double.isNaN(x)) {
1946             return 0;
1947         }
1948         return x == Double.POSITIVE_INFINITY ? Math.nextDown(x) : Math.nextUp(x);
1949     }
1950 
1951     @Test
1952     void testAtanhEdgeConditions() {
1953         // Hits the edge case when imaginary == 0 but real != 0 or 1
1954         final Complex c = Complex.ofCartesian(2, 0).atanh();
1955         // Answer from g++
1956         Assertions.assertEquals(0.54930614433405489, c.getReal());
1957         Assertions.assertEquals(1.5707963267948966, c.getImaginary());
1958     }
1959 
1960     @Test
1961     void testAtanhAssumptions() {
1962         // Compute the same constants used by atanh
1963         final double safeUpper = Math.sqrt(Double.MAX_VALUE) / 2;
1964         final double safeLower = Math.sqrt(Double.MIN_NORMAL) * 2;
1965 
1966         // Can we assume (1+x) = x when x is large
1967         Assertions.assertEquals(safeUpper, 1 + safeUpper);
1968         // Can we assume (1-x) = -x when x is large
1969         Assertions.assertEquals(-safeUpper, 1 - safeUpper);
1970         // Can we assume (y^2/x) = 0 when y is small and x is large
1971         Assertions.assertEquals(0, safeLower * safeLower / safeUpper);
1972         // Can we assume (1-x)^2/y + y = y when x <= 1. Try with x = 0.
1973         Assertions.assertEquals(safeUpper, 1 / safeUpper + safeUpper);
1974         // Can we assume (4+y^2) = 4 when y is small
1975         Assertions.assertEquals(4, 4 + safeLower * safeLower);
1976         // Can we assume (1-x)^2 = 1 when x is small
1977         Assertions.assertEquals(1, (1 - safeLower) * (1 - safeLower));
1978         // Can we assume 1 - y^2 = 1 when y is small
1979         Assertions.assertEquals(1, 1 - safeLower * safeLower);
1980         // Can we assume Math.log1p(4 * x / y / y) = (4 * x / y / y) when big y and small
1981         // x
1982         final double result = 4 * safeLower / safeUpper / safeUpper;
1983         Assertions.assertEquals(result, Math.log1p(result));
1984         Assertions.assertEquals(result, result - result * result / 2, "Expected log1p Taylor series to be redundant");
1985         // Can we assume if x != 1 then (x-1) is valid for multiplications.
1986         Assertions.assertNotEquals(0, 1 - Math.nextUp(1));
1987         Assertions.assertNotEquals(0, 1 - Math.nextDown(1));
1988     }
1989 
1990     @Test
1991     void testCoshSinhTanhAssumptions() {
1992         // Use the same constants used to approximate cosh/sinh with e^|x| / 2
1993         final double safeExpMax = 708;
1994 
1995         final double big = Math.exp(safeExpMax);
1996         final double small = Math.exp(-safeExpMax);
1997 
1998         // Overflow assumptions
1999         Assertions.assertTrue(Double.isFinite(big));
2000         Assertions.assertTrue(Double.isInfinite(Math.exp(safeExpMax + 2)));
2001 
2002         // Can we assume cosh(x) = (e^x + e^-x) / 2 = e^|x| / 2
2003         Assertions.assertEquals(big + small, big);
2004         Assertions.assertEquals(Math.cosh(safeExpMax), big / 2);
2005         Assertions.assertEquals(Math.cosh(-safeExpMax), big / 2);
2006 
2007         // Can we assume sinh(x) = (e^x - e^-x) / 2 = sign(x) * e^|x| / 2
2008         Assertions.assertEquals(big - small, big);
2009         Assertions.assertEquals(small - big, -big);
2010         Assertions.assertEquals(Math.sinh(safeExpMax), big / 2);
2011         Assertions.assertEquals(Math.sinh(-safeExpMax), -big / 2);
2012 
2013         // Can we assume sinh(x/2) * cosh(x/2) is finite
2014         // Can we assume sinh(x/2)^2 is finite
2015         Assertions.assertTrue(Double.isFinite(Math.sinh(safeExpMax / 2) * Math.cosh(safeExpMax / 2)));
2016         Assertions.assertTrue(Double.isFinite(Math.sinh(safeExpMax / 2) * Math.sinh(safeExpMax / 2)));
2017 
2018         // Will 2.0 / e^|x| / e^|x| underflow
2019         Assertions.assertNotEquals(0.0, 2.0 / big);
2020         Assertions.assertEquals(0.0, 2.0 / big / big);
2021 
2022         // This is an assumption used in sinh/cosh.
2023         // Will 3 * (e^|x|/2) * y overflow for any positive y
2024         Assertions.assertTrue(Double.isFinite(0.5 * big * Double.MIN_VALUE * big));
2025         Assertions.assertTrue(Double.isInfinite(0.5 * big * Double.MIN_VALUE * big * big));
2026 
2027         // Assume the sign of sin(2y) = sin(y) * cos(y) when |y| < pi/2
2028         for (final double y : new double[] {Math.PI / 2, Math.PI / 4, 1.0, 0.5, 0.0}) {
2029             Assertions.assertEquals(Math.signum(Math.sin(2 * y)), Math.signum(Math.sin(y) * Math.cos(y)));
2030             Assertions.assertEquals(Math.signum(Math.sin(2 * -y)), Math.signum(Math.sin(-y) * Math.cos(-y)));
2031         }
2032 
2033         // tanh: 2.0 / Double.MAX_VALUE does not underflow.
2034         // Thus 2 sin(2y) / e^2|x| can be computed when e^2|x| only just overflows
2035         Assertions.assertTrue(2.0 / Double.MAX_VALUE > 0);
2036     }
2037 
2038     /**
2039      * Test that sin and cos are linear around zero. This can be used for fast computation
2040      * of sin and cos together when |x| is small.
2041      */
2042     @Test
2043     void testSinCosLinearAssumptions() {
2044         // Are cos and sin linear around zero?
2045         // If cos is still 1 then since d(sin) dx = cos then sin is linear.
2046         Assertions.assertEquals(1.0, Math.cos(Double.MIN_NORMAL));
2047         Assertions.assertEquals(Double.MIN_NORMAL, Math.sin(Double.MIN_NORMAL));
2048 
2049         // Are cosh and sinh linear around zero?
2050         // If cosh is still 1 then since d(sinh) dx = cosh then sinh is linear.
2051         Assertions.assertEquals(1.0, Math.cosh(Double.MIN_NORMAL));
2052         Assertions.assertEquals(Double.MIN_NORMAL, Math.sinh(Double.MIN_NORMAL));
2053     }
2054 
2055     /**
2056      * Test the abs and sqrt functions are consistent. The definition of sqrt uses abs and
2057      * the result should be computed using the same representation of the complex number's
2058      * magnitude (abs). If the sqrt function uses a simple representation
2059      * {@code sqrt(x^2 + y^2)} then this may have a 1 ulp or more difference from the high
2060      * accuracy result computed by abs. This will propagate to create differences in sqrt.
2061      *
2062      * <p>Note: This test is separated from the similar test for log to allow testing
2063      * different numbers.
2064      */
2065     @Test
2066     void testAbsVsSqrt() {
2067         final UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create();
2068         // Note: All methods implement scaling to ensure the magnitude can be computed.
2069         // Try very large or small numbers that will over/underflow to test that the
2070         // scaling
2071         // is consistent. Note that:
2072         // - sqrt will reduce the size of the real and imaginary
2073         // components when |z|>1 and increase them when |z|<1.
2074 
2075         // Each sample fails approximately 3% of the time if using a standard x^2+y^2 in
2076         // sqrt()
2077         // and high accuracy representation in abs().
2078         // Use 1000 samples to ensure the behavior is OK.
2079         // Do not use data which will over/underflow so we can use a simple computation in
2080         // the test
2081         assertAbsVsSqrt(1000,
2082             () -> Complex.ofCartesian(createFixedExponentNumber(rng, 1000), createFixedExponentNumber(rng, 1000)));
2083         assertAbsVsSqrt(1000,
2084             () -> Complex.ofCartesian(createFixedExponentNumber(rng, -1000), createFixedExponentNumber(rng, -1000)));
2085     }
2086 
2087     private static void assertAbsVsSqrt(int samples, Supplier<Complex> supplier) {
2088         // Note: All methods implement scaling to ensure the magnitude can be computed.
2089         // Try very large or small numbers that will over/underflow to test that the
2090         // scaling
2091         // is consistent.
2092         for (int i = 0; i < samples; i++) {
2093             final Complex z = supplier.get();
2094             final double abs = z.abs();
2095             final double x = Math.abs(z.getReal());
2096             final double y = Math.abs(z.getImaginary());
2097 
2098             // Target the formula provided in the documentation for sqrt:
2099             // sqrt(x + iy)
2100             // t = sqrt( 2 (|x| + |x + iy|) )
2101             // if x >= 0: (t/2, y/t)
2102             // else : (|y| / t, t/2 * sgn(y))
2103             // Note this is not the definitional polar computation using absolute and
2104             // argument:
2105             // real = sqrt(|z|) * cos(0.5 * arg(z))
2106             // imag = sqrt(|z|) * sin(0.5 * arg(z))
2107             final Complex c = z.sqrt();
2108             final double t = Math.sqrt(2 * (x + abs));
2109             if (z.getReal() >= 0) {
2110                 Assertions.assertEquals(t / 2, c.getReal());
2111                 Assertions.assertEquals(z.getImaginary() / t, c.getImaginary());
2112             } else {
2113                 Assertions.assertEquals(y / t, c.getReal());
2114                 Assertions.assertEquals(Math.copySign(t / 2, z.getImaginary()), c.getImaginary());
2115             }
2116         }
2117     }
2118 
2119     /**
2120      * Test the abs and log functions are consistent. The definition of log uses abs and
2121      * the result should be computed using the same representation of the complex number's
2122      * magnitude (abs). If the log function uses a simple representation
2123      * {@code sqrt(x^2 + y^2)} then this may have a 1 ulp or more difference from the high
2124      * accuracy result computed by abs. This will propagate to create differences in log.
2125      *
2126      * <p>Note: This test is separated from the similar test for sqrt to allow testing
2127      * different numbers.
2128      */
2129     @Test
2130     void testAbsVsLog() {
2131         final UniformRandomProvider rng = RandomSource.XO_RO_SHI_RO_128_PP.create();
2132         // Note: All methods implement scaling to ensure the magnitude can be computed.
2133         // Try very large or small numbers that will over/underflow to test that the
2134         // scaling
2135         // is consistent. Note that:
2136         // - log will set the real component using log(|z|). This will massively reduce
2137         // the magnitude when |z| >> 1. Highest accuracy will be when |z| is as large
2138         // as possible before computing the log.
2139 
2140         // No test around |z| == 1 as a high accuracy computation is required:
2141         // Math.log1p(x*x+y*y-1)
2142 
2143         // Each sample fails approximately 25% of the time if using a standard x^2+y^2 in
2144         // log()
2145         // and high accuracy representation in abs(). Use 100 samples to ensure the
2146         // behavior is OK.
2147         assertAbsVsLog(100,
2148             () -> Complex.ofCartesian(createFixedExponentNumber(rng, 1022), createFixedExponentNumber(rng, 1022)));
2149         assertAbsVsLog(100,
2150             () -> Complex.ofCartesian(createFixedExponentNumber(rng, -1022), createFixedExponentNumber(rng, -1022)));
2151     }
2152 
2153     private static void assertAbsVsLog(int samples, Supplier<Complex> supplier) {
2154         // Note: All methods implement scaling to ensure the magnitude can be computed.
2155         // Try very large or small numbers that will over/underflow to test that the
2156         // scaling
2157         // is consistent.
2158         for (int i = 0; i < samples; i++) {
2159             final Complex z = supplier.get();
2160             final double abs = z.abs();
2161             final double x = Math.abs(z.getReal());
2162             final double y = Math.abs(z.getImaginary());
2163 
2164             // log(x + iy) = log(|x + i y|) + i arg(x + i y)
2165             // Only test the real component
2166             final Complex c = z.log();
2167             Assertions.assertEquals(Math.log(abs), c.getReal());
2168         }
2169     }
2170 
2171     /**
2172      * Creates a number in the range {@code [1, 2)} with up to 52-bits in the mantissa.
2173      * Then modifies the exponent by the given amount.
2174      *
2175      * @param rng Source of randomness
2176      * @param exponent Amount to change the exponent (in range [-1023, 1023])
2177      * @return the number
2178      */
2179     private static double createFixedExponentNumber(UniformRandomProvider rng, int exponent) {
2180         return Double.longBitsToDouble((rng.nextLong() >>> 12) | ((1023L + exponent) << 52));
2181     }
2182 }