1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     math_Equation.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 46347 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nn/math.h>
17 
18 #include <nn/math/math_Equation.h>
19 #include <nn/math/math_Arithmetic.h>
20 #include <nn/math/math_Triangular.h>
21 #include <nn/types.h>
22 
23 namespace nn { namespace math {
24 
25 
26 namespace
27 {
28     // Maximum error for determining equality
29     const f32 EPS = 2e-4f;
30 
31 
32     /*!--------------------------------------------------------------------------*
33       Name:        spow<int>
34 
35 
36 
37 
38 
39 
40 
41 
42      *---------------------------------------------------------------------------*/
43     template <int idx>
44     NN_MATH_INLINE f32
spow(f32 x)45     spow(f32 x)
46     {
47         return spow<idx-1>(x) * x;
48     }
49 
50     template <>
51     NN_MATH_INLINE f32
spow(f32 x)52     spow<1>(f32 x)
53     {
54         return x;
55     }
56 
57     /*!--------------------------------------------------------------------------*
58       Name:        SolveEquation2
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70      *---------------------------------------------------------------------------*/
71     int
SolveEquation2(f32 * root,f32 b,f32 c)72     SolveEquation2(f32* root, /*f32 a==1,*/ f32 b, f32 c)
73     {
74         if( b == 0 )
75         {
76             if( c < -EPS )      // c < 0
77             {
78                 f32 r_c = FSqrt(-c);
79                 root[0] =   r_c;
80                 root[1] = - r_c;
81                 return 2;
82             }
83             else if( c <= EPS ) // c == 0
84             {
85                 root[0] = 0;
86                 return 1;
87             }
88             else                // c > 0
89             {
90                 return 0;
91             }
92         }
93         else
94         {
95             f32 A = b / 2;
96             f32 B = c / spow<2>(A);
97             f32 D = 1 - B;
98 
99             if( D > 0 )
100             {
101                 f32 C = -1 - FSqrt(D);
102 
103                 root[0] = A * C;
104                 root[1] = A * B / C;
105 
106                 return 2;
107             }
108             else if( FAbs(D) < EPS )
109             {
110                 root[0] = -A;
111 
112                 return 1;
113             }
114             else
115             {
116                 return 0;
117             }
118         }
119     }
120 
121 
122 
123     /*!--------------------------------------------------------------------------*
124       Name:        SolveEquation3
125 
126 
127 
128 
129 
130 
131 
132      *---------------------------------------------------------------------------*/
133     f32
SolveEquation3(f32 b,f32 c,f32 d)134     SolveEquation3(/*f32* root, f32 a==1,*/ f32 b, f32 c, f32 d)
135     {
136         f32 q   = (spow<2>(b) - 3 * c) / 9;
137         f32 r   = (2 * spow<3>(b) - 9 * b * c + 27 * d) / 54;
138         f32 D   = spow<3>(q) - spow<2>(r);
139         f32 b_3 = b / 3;
140 
141         if( D > EPS )
142         {
143             f32 theta   = AcosRad( r / FSqrt(spow<3>(q)) );
144             f32 theta_3 = theta/3;
145             f32 r_q2    = -2 * FSqrt(q);
146 
147             return r_q2 * CosRad(theta_3) - b_3;
148         }
149         else if( D < - EPS )
150         {
151             f32 r3_Dr = FCbrt( FSqrt(- D) + FAbs(r) );
152             f32 xp    = r3_Dr + q / r3_Dr;
153 
154             return - FCopySign(xp, r) - b_3;
155         }
156         else
157         {
158             f32 xp  = FSqrt(q);
159 
160             return FCopySign(xp, r) - b_3;
161         }
162     }
163 }
164 
165 
166 
167 
168 
169 
170 
171 /*!--------------------------------------------------------------------------*
172   Name:        SolveEquation2
173 
174 
175 
176 
177 
178 
179 
180 
181 
182 
183 
184  *---------------------------------------------------------------------------*/
185 int
SolveEquation2(f32 * root,f32 a,f32 b,f32 c)186 SolveEquation2(f32* root, f32 a, f32 b, f32 c)
187 {
188     NN_POINTER_TASSERT_(root);
189     NN_TASSERT_( a != 0 );
190 
191     if( b != 0 )
192     {
193         // Standard solution formula
194         //   With the standard solution equation, x = {-b ± √(b^2 - 4ac)} / 2a, when finding a solution when the sign of -b and the ± of the -b ± √(omitted) portion, subtraction of close value occurs and precision drops a lot.
195         //
196         //
197         // To avoid this, set one side of the solution to x = b{-1 - √(1 - 4ac/b^2)} / 2a, factor out b, and solve.
198         //
199         //
200         // For the other side, apply the relationship between the solution and the coefficients (αβ = a / c) to the above formula, and solve using x = 2c / b{-1 - √(1 - 4ac/b^2)}.
201         //
202         //
203         //
204         //
205 
206         f32 A = b / (2 * a);
207         f32 B = c / (a * A * A);
208         f32 D = 1 - B;
209 
210         if( D > EPS )
211         {
212             f32 C = -1 - FSqrt(D);
213 
214             root[0] = A * C;
215             root[1] = A * B / C;
216 
217             return 2;
218         }
219         else if( D >= - EPS )
220         {
221             root[0] = -A;
222 
223             return 1;
224         }
225         else
226         {
227             return 0;
228         }
229     }
230     else
231     {
232         // When using this method, division by 0 will occur if b = 0, so handle as appropriate.
233 
234         f32 c_a = - c / a;
235 
236         if( c_a > EPS )
237         {
238             f32 r_c_a = FSqrt(c_a);
239             root[0] = + r_c_a;
240             root[1] = - r_c_a;
241             return 2;
242         }
243         else if( c_a >= - EPS )
244         {
245             root[0] = 0;
246             return 1;
247         }
248         else
249         {
250             return 0;
251         }
252     }
253 }
254 
255 
256 
257 /*!--------------------------------------------------------------------------*
258   Name:        SolveEquation3
259 
260 
261 
262 
263 
264 
265 
266 
267 
268 
269 
270  *---------------------------------------------------------------------------*/
271 int
SolveEquation3(f32 * root,f32 a,f32 b,f32 c,f32 d)272 SolveEquation3(f32* root, f32 a, f32 b, f32 c, f32 d)
273 {
274     NN_POINTER_TASSERT_(root);
275     NN_TASSERT_( a != 0 );
276 
277     //---- Divide both sides of the equation by the coefficient of the term of highest degree so that the coefficient for that term is 1.
278     b /= a;
279     c /= a;
280     d /= a;
281 
282     //---- Find the value of the fundamental descriminant D
283     f32 q = (spow<2>(b) - 3 * c) / 9;
284     f32 r = (2 * spow<3>(b) - 9 * b * c + 27 * d) / 54;
285     f32 D = spow<3>(q) - spow<2>(r);
286 
287     f32 b_3 = b / 3;
288 
289     if( D > EPS )
290     {
291         f32 theta   = AcosRad( r / FSqrt(spow<3>(q)) );
292         f32 theta_3 = theta/3;
293         f32 r_q2    = -2 * FSqrt(q);
294 
295         root[0] = r_q2 * CosRad(theta_3 + 0 * F_PI / 3) - b_3;
296         root[1] = r_q2 * CosRad(theta_3 + 2 * F_PI / 3) - b_3;
297         root[2] = r_q2 * CosRad(theta_3 + 4 * F_PI / 3) - b_3;
298 
299         return 3;
300     }
301     else if( D < - EPS )
302     {
303         f32 r3_Dr = FCbrt( FSqrt(- D) + FAbs(r) );
304         f32 xp    = r3_Dr + q / r3_Dr;
305 
306         root[0] = - FCopySign(xp, r) - b_3;
307 
308         return 1;
309     }
310     else
311     {
312         f32 xp  = FSqrt(q);
313         f32 sxp = FCopySign(xp, r);
314 
315         root[0] =  1 * sxp - b_3;
316         root[1] = -2 * sxp - b_3;
317 
318         return 2;
319     }
320 }
321 
322 
323 
324 /*!--------------------------------------------------------------------------*
325   Name:        SolveEquation4
326 
327 
328 
329 
330 
331 
332 
333 
334 
335 
336 
337 
338  *---------------------------------------------------------------------------*/
339 int
SolveEquation4(f32 * root,f32 a,f32 b,f32 c,f32 d,f32 e)340 SolveEquation4(f32* root, f32 a, f32 b, f32 c, f32 d, f32 e)
341 {
342     NN_POINTER_TASSERT_(root);
343     NN_TASSERT_( a != 0 );
344 
345     f32 m, n, y;
346 
347     //---- Divide both sides of the equation by the coefficient of the term of highest degree so that the coefficient for that term is 1.
348     b /= a;
349     c /= a;
350     d /= a;
351     e /= a;
352 
353     //---- Complete the square
354     {
355         // Positing t = x + b/4 leads to x = t - b/4
356         // The third degree term disappears if the above is used to substitute for x and then simplification is applied.
357         //   t^4 + pt^2 + qt + r = 0
358         // results. Here, p, q, r are each as given below.
359 
360         f32 p = - 3.f * spow<2>(b) / 8 + c;
361         f32 q = spow<3>(b) / 8 - b * c / 2 + d;
362         f32 r = - 3.f * spow<4>(b) / 256 + spow<2>(b) * c / 16 - b * d / 4 + e;
363 
364 
365         // Consider completing the square in a form such as the following for the formula for t.
366         //   (t^2 + y)^2 = (mt + n)^2   ------- (1)
367 
368         if( q != 0 )
369         {
370             // First, for any value y for t^2
371             //   (t^2 + y)^2 = 2yt^2 + y^2 - (pt^2 + qt + r)
372             //               = (2y - p)t^2 + qt + (y^2 - r)   ------- (2)
373             // can be used to complete the square.
374             // To finally complete the square on the right side as well, it needs to meet the fundamental descriminant of the quadratic on the right side D = 0, and use:
375             //   q^2 - 4(2y - p)(y^2 - r) = 0
376             // and find y that satisfies the above formula. Organizing for y gives
377             //   y^3 - (p/2)y^2 - ry + (pr/2 - (q^2)/8) = 0
378             // Solving this cubic equation, one solution can be used as y.
379 
380             y = SolveEquation3(-p / 2, -r, p * r / 2 - spow<2>(q) / 8);
381 
382 
383             // (1) Expanding the right side gives
384             //   (mt + n)^2 = m^2t^2 + 2mnt + n^2
385             // Get the following by comparing this with Equation (2).
386 
387             n = FSqrt(spow<2>(y) - r);
388             m = -q / (2 * n);
389         }
390         else
391         {
392             // When q = 0, y = p/2, ±sqrt(r) results when solving the above cubic equation
393             // Given SolveEquation3, if ±sqrt(r) is selected, n = 0 results,
394             // and division by 0 occurs.
395             // Finally, if p = q = r = 0, then n must equal 0.
396             // Avoid all this.
397 
398             y = p / 2;
399             n = FSqrt(spow<2>(y) - r);
400             m = 0;
401         }
402     }
403 
404     // Since the given equation has been converted to the form of Equation 1, solve it.
405     // Taking the square root of both sides of Equation 1 results in,
406     //   t^2 + y = ±(mt + n)
407     // This can be represented using two quadratic formulas:
408     //   t^2 + mt + (y + n) = 0
409     //   t^2 - mt + (y - n) = 0
410     // The value of t is found by solving these.
411     // x is then found using x = t - b/4.
412 
413     int nRoot = 0;
414     f32 b4    = b / 4;
415 
416     //---- Quadratic equation 1
417     {
418         f32 root01[2];
419 
420         int nRoot01 = SolveEquation2(root01, m, y + n);
421 
422         root[nRoot]   = root01[0] - b4;
423         root[nRoot+1] = root01[1] - b4;
424         nRoot += nRoot01;
425     }
426 
427     //---- Quadratic equation 2
428     {
429         f32 root23[2];
430 
431         int nRoot23 = SolveEquation2(root23, -m, y - n);
432 
433         root[nRoot]   = root23[0] - b4;
434         root[nRoot+1] = root23[1] - b4;
435         nRoot += nRoot23;
436     }
437 
438     return nRoot;
439 }
440 
441 
442 }}  // nw::math
443