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