View Javadoc
1   package org.djutils.polynomialroots;
2   
3   import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4   import static org.junit.jupiter.api.Assertions.assertEquals;
5   
6   import org.djutils.complex.Complex;
7   import org.junit.jupiter.api.Test;
8   
9   /**
10   * TestPolynomialRoots2.java.
11   * <p>
12   * Copyright (c) 2020-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
13   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
14   * </p>
15   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
16   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
17   */
18  public class TestPolynomialRoots2
19  {
20  
21      /**
22       * Test the solver for quadratic (and simpler) cases and ensure that the cubic solver falls back to the quadratic solver
23       * when appropriate.
24       */
25      @Test
26      public void quadraticTest()
27      {
28          double[] paramValues = new double[] {0, 1, 2, 3, 4, Math.PI, -Math.E, 0.0001, 10000};
29          for (double a : paramValues)
30          {
31              for (double b : paramValues)
32              {
33                  for (double c : paramValues)
34                  {
35                      // System.out.print(a + " x^2 + " + b + " x " + c + " = 0");
36                      Complex[] roots = PolynomialRoots2.quadraticRoots(a, b, c);
37                      int expectedNumber = a != 0 ? 2 : b != 0 ? 1 : 0;
38                      assertEquals(expectedNumber, roots.length, "number of roots");
39                      // System.out.print(": " + expectedNumber + " solutions");
40                      // Check that each root is indeed a root
41                      for (Complex root : roots)
42                      {
43                          // System.out.print(" " + root);
44                          Complex result = root.times(root).times(a).plus(root.times(b)).plus(c);
45                          double margin = Math.max(root.norm() / 1e6, 1E-300);
46                          assertEquals(0, result.re, margin, "root is a root of the equation; re");
47                          assertEquals(0, result.im, margin, "root is a root of the equation; im");
48                      }
49                      // System.out.println();
50                      Complex[] alternate = PolynomialRoots2.cubicRootsDurandKerner(0, a, b, c);
51                      assertArrayEquals(roots, alternate, "cubic solver returns same results as quadratic solver if a is 0");
52                  }
53              }
54          }
55          double q1 = 3 * Math.sqrt(Double.MAX_VALUE);
56          Complex[] roots = PolynomialRoots2.quadraticRoots(q1, 1);
57          assertEquals(2, roots.length, "two roots");
58          assertEquals(0, roots[0].re, 0.0001, "first root is 0: re");
59          assertEquals(0, roots[0].im, 0.0001, "first root is 0: im");
60          // System.out.println(roots[1]);
61          assertEquals(-q1, roots[1].re, q1 / 1e7, "second root is 0: re");
62          assertEquals(0, roots[1].im, 0.0001, "second root is 0: im");
63      }
64  
65      /**
66       * Test the cubic solver.
67       */
68      @Test
69      public void cubicTestDurandKerner()
70      {
71          double[] paramValues = new double[] {0, 1, 2, 3, 4, Math.PI, -Math.E, 0.001, 1000};
72          for (double a : paramValues)
73          {
74              for (double b : paramValues)
75              {
76                  for (double c : paramValues)
77                  {
78                      for (double d : paramValues)
79                      {
80                          // System.out.print(a + " x^3 " + b + " x^2 + " + c + " x + " + d + " = 0");
81                          Complex[] roots = PolynomialRoots2.cubicRootsDurandKerner(a, b, c, d);
82                          int expectedNumber = a != 0 ? 3 : b != 0 ? 2 : c != 0 ? 1 : 0;
83                          assertEquals(expectedNumber, roots.length, "number of roots");
84                          // System.out.print(": " + expectedNumber + " solutions");
85                          // Check that each root is indeed a root
86                          for (Complex root : roots)
87                          {
88                              // System.out.print(" " + root);
89                              Complex result = root.times(root).times(root).times(a).plus(root.times(root).times(b))
90                                      .plus(root.times(c)).plus(d);
91                              double margin = Math.max(root.norm() / 1e6, 1E-300);
92                              assertEquals(0, result.re, margin, "root is a root of the equation; re");
93                              assertEquals(0, result.im, margin, "root is a root of the equation; im");
94                          }
95                          // System.out.println();
96                      }
97                  }
98              }
99          }
100     }
101 
102     /**
103      * Test the quartic solver.
104      */
105     @Test
106     public void quarticTestDurandKerner()
107     {
108         double[] paramValues = new double[] {0, 1, 2, 3, 4, Math.PI, -Math.E, 0.01, 100};
109         for (double a : paramValues)
110         {
111             for (double b : paramValues)
112             {
113                 for (double c : paramValues)
114                 {
115                     for (double d : paramValues)
116                     {
117                         for (double e : paramValues)
118                         {
119                             // System.out.print(a + " x^4 + " + b + " x^3 + " + c + " x^2 + " + d + " x + " + e + " = 0");
120                             Complex[] roots = PolynomialRoots2.quarticRootsDurandKerner(a, b, c, d, e);
121                             int expectedNumber = a != 0 ? 4 : b != 0 ? 3 : c != 0 ? 2 : d != 0 ? 1 : 0;
122                             assertEquals(expectedNumber, roots.length, "number of roots");
123                             // System.out.print(": " + expectedNumber + " solutions");
124                             // Check that each root is indeed a root
125                             for (Complex root : roots)
126                             {
127                                 // System.out.print(" " + root);
128                                 // TODO compute result with less chance of loss of precision
129                                 Complex result = root.times(root).times(root).times(root).times(a)
130                                         .plus(root.times(root).times(root).times(b)).plus(root.times(root).times(c))
131                                         .plus(root.times(d)).plus(e);
132                                 double margin = Math.max(root.norm() / 2e5, 1E-300);
133                                 assertEquals(0, result.re, margin, a + " x^4 + " + b + " x^3 + " + c + " x^2 + " + d + " x + " + e
134                                                 + " = 0 : root is a root of the equation; re");
135                                 assertEquals(0, result.im, margin, a + " x^4 + " + b + " x^3 + " + c + " x^2 + " + d + " x + " + e
136                                                 + " = 0 : root is a root of the equation; im");
137                             }
138                             // System.out.println();
139                         }
140                     }
141                 }
142             }
143         }
144     }
145 
146     /**
147      * Test the cubic solver.
148      */
149     @Test
150     public void cubicTestAberthEhrlich()
151     {
152         double[] paramValues = new double[] {0, 1, 2, 3, 4, Math.PI, -Math.E, 0.001, 1000};
153         for (double a : paramValues)
154         {
155             for (double b : paramValues)
156             {
157                 for (double c : paramValues)
158                 {
159                     for (double d : paramValues)
160                     {
161                         // System.out.print(a + " x^3 " + b + " x^2 + " + c + " x + " + d + " = 0");
162                         Complex[] roots = PolynomialRoots2.cubicRootsAberthEhrlich(a, b, c, d);
163                         int expectedNumber = a != 0 ? 3 : b != 0 ? 2 : c != 0 ? 1 : 0;
164                         assertEquals(expectedNumber, roots.length, "number of roots");
165                         // System.out.print(": " + expectedNumber + " solutions");
166                         // Check that each root is indeed a root
167                         for (Complex root : roots)
168                         {
169                             // System.out.print(" " + root);
170                             Complex result = root.times(root).times(root).times(a).plus(root.times(root).times(b))
171                                     .plus(root.times(c)).plus(d);
172                             double margin = Math.max(root.norm() / 1e6, 1E-300);
173                             assertEquals(0, result.re, margin, "root is a root of the equation; re");
174                             assertEquals(0, result.im, margin, "root is a root of the equation; im");
175                         }
176                         // System.out.println();
177                     }
178                 }
179             }
180         }
181     }
182 
183     /**
184      * Test the quartic solver.
185      */
186     @Test
187     public void quarticTestAberthEhrlich()
188     {
189         double[] paramValues = new double[] {0, 1, 2, 3, 4, Math.PI, -Math.E, 0.01, 100};
190         for (double a : paramValues)
191         {
192             for (double b : paramValues)
193             {
194                 for (double c : paramValues)
195                 {
196                     for (double d : paramValues)
197                     {
198                         for (double e : paramValues)
199                         {
200                             // System.out.print(a + " x^4 + " + b + " x^3 + " + c + " x^2 + " + d + " x + " + e + " = 0");
201                             Complex[] roots = PolynomialRoots2.quarticRootsAberthEhrlich(a, b, c, d, e);
202                             int expectedNumber = a != 0 ? 4 : b != 0 ? 3 : c != 0 ? 2 : d != 0 ? 1 : 0;
203                             assertEquals(expectedNumber, roots.length, "number of roots");
204                             // System.out.print(": " + expectedNumber + " solutions");
205                             // Check that each root is indeed a root
206                             for (Complex root : roots)
207                             {
208                                 // System.out.print(" " + root);
209                                 // TODO compute result with less chance of loss of precision
210                                 Complex result = root.times(root).times(root).times(root).times(a)
211                                         .plus(root.times(root).times(root).times(b)).plus(root.times(root).times(c))
212                                         .plus(root.times(d)).plus(e);
213                                 double margin = Math.max(root.norm() / 2e5, 1E-300);
214                                 assertEquals(0, result.re, margin, a + " x^4 + " + b + " x^3 + " + c + " x^2 + " + d + " x + " + e
215                                                 + " = 0 : root is a root of the equation; re");
216                                 assertEquals(0, result.im, margin, a + " x^4 + " + b + " x^3 + " + c + " x^2 + " + d + " x + " + e
217                                                 + " = 0 : root is a root of the equation; im");
218                             }
219                             // System.out.println();
220                         }
221                     }
222                 }
223             }
224         }
225     }
226 
227     /**
228      * Test the cubic solver.<br>
229      * XXX: Not active yet. Code is in development
230      */
231     public void cubicTestNewtonFactor()
232     {
233         double[] paramValues = new double[] {0, 1, 2, 3, 4, Math.PI, -Math.E, 0.001, 1000};
234         for (double a : paramValues)
235         {
236             for (double b : paramValues)
237             {
238                 for (double c : paramValues)
239                 {
240                     for (double d : paramValues)
241                     {
242                         // System.out.print(a + " x^3 + " + b + " x^2 + " + c + " x + " + d + " = 0");
243                         Complex[] roots = PolynomialRoots2.cubicRootsNewtonFactor(a, b, c, d);
244                         int expectedNumber = a != 0 ? 3 : b != 0 ? 2 : c != 0 ? 1 : 0;
245                         assertEquals(expectedNumber, roots.length, "number of roots");
246                         // System.out.print(": " + expectedNumber + " solutions");
247                         // Check that each root is indeed a root
248                         for (Complex root : roots)
249                         {
250                             // System.out.print(" " + root);
251                             Complex result = root.times(root).times(root).times(a).plus(root.times(root).times(b))
252                                     .plus(root.times(c)).plus(d);
253                             double margin = Math.max(root.norm() / 1e4, 1E-3);
254                             String eq = a + " x^3 + " + b + " x^2 + " + c + " x + " + d + " = 0";
255                             assertEquals(0, result.re, margin, eq + ", root=" + root + ", result=" + result + "; re");
256                             assertEquals(0, result.im, margin, eq + ", root=" + root + ", result=" + result + "; im");
257                         }
258                         // System.out.println();
259                     }
260                 }
261             }
262         }
263     }
264 
265 }