1 module gfm.math.funcs;
2 
3 import std.math,
4        std.traits;
5 
6 /// Returns: minimum of a and b.
7 T min(T)(T a, T b) pure nothrow
8 {
9     return a < b ? a : b;
10 }
11 
12 /// Returns: maximum of a and b.
13 T max(T)(T a, T b) pure nothrow
14 {
15     return a > b ? a : b;
16 }
17 
18 /// Convert from radians to degrees.
19 T degrees(T)(T x) pure if (!isIntegral!T)
20 {
21     return x * (180 / PI);
22 }
23 
24 /// Convert from degrees to radians.
25 T radians(T)(T x) pure nothrow if (!isIntegral!T)
26 {
27     return x * (PI / 180);
28 }
29 
30 /// Linear interpolation, akin to GLSL's mix.
31 S lerp(S, T)(S a, S b, T t) pure nothrow
32 {
33     return t * b + (1 - t) * a;
34 }
35 
36 /// Clamp x in [min, max], akin to GLSL's clamp.
37 T clamp(T)(T x, T min, T max) pure nothrow
38 {
39     if (x < min)
40         return min;
41     else if (x > max)
42         return max;
43     else
44         return x;
45 }
46 
47 /// Integer truncation.
48 long ltrunc(real x) nothrow // may be pure but trunc isn't pure
49 {
50     return cast(long)(trunc(x));
51 }
52 
53 /// Integer flooring.
54 long lfloor(real x) nothrow // may be pure but floor isn't pure
55 {
56     return cast(long)(floor(x));
57 }
58 
59 /// Returns: Fractional part of x.
60 T fract(T)(real x) nothrow
61 {
62     return x - lfloor(x);
63 }
64 
65 /// Safe asin: input clamped to [-1, 1]
66 T safeAsin(T)(T x) pure nothrow
67 {
68     return asin(clamp!T(x, -1, 1));
69 }
70 
71 /// Safe acos: input clamped to [-1, 1]
72 T safeAcos(T)(T x) pure nothrow
73 {
74     return acos(clamp!T(x, -1, 1));
75 }
76 
77 /// Same as GLSL step function.
78 /// 0.0 is returned if x < edge, and 1.0 is returned otherwise.
79 T step(T)(T edge, T x) pure nothrow
80 {
81     return (x < edge) ? 0 : 1;
82 }
83 
84 /// Same as GLSL smoothstep function.
85 /// See: http://en.wikipedia.org/wiki/Smoothstep
86 T smoothStep(T)(T a, T b, T t) pure nothrow
87 {
88     if (t <= a) 
89         return 0;
90     else if (t >= b) 
91         return 1;
92     else
93     {
94         T x = (t - a) / (b - a);
95         return x * x * (3 - 2 * x);
96     }
97 }
98 
99 /// Returns: true of i is a power of 2.
100 bool isPowerOf2(T)(T i) nothrow if (isIntegral!T)
101 {
102     assert(i >= 0);
103     return (i != 0) && ((i & (i - 1)) == 0);
104 }
105 
106 /// Integer log2
107 /// TODO: use bt intrinsics
108 int ilog2(T)(T i) nothrow if (isIntegral!T)
109 {
110     assert(i > 0);
111     assert(isPowerOf2(i));
112     int result = 0;
113     while (i > 1)
114     {
115         i = i / 2;
116         result = result + 1;
117     }
118     return result;
119 }
120 
121 /// Computes next power of 2.
122 int nextPowerOf2(int i) nothrow
123 {
124     int v = i - 1;
125     v |= v >> 1;
126     v |= v >> 2;
127     v |= v >> 4;
128     v |= v >> 8;
129     v |= v >> 16;
130     v++;
131     assert(isPowerOf2(v));
132     return v;
133 }
134 
135 /// Computes next power of 2.
136 long nextPowerOf2(long i) nothrow
137 {
138     long v = i - 1;
139     v |= v >> 1;
140     v |= v >> 2;
141     v |= v >> 4;
142     v |= v >> 8;
143     v |= v >> 16;
144     v |= v >> 32;
145     v++;
146     assert(isPowerOf2(v));
147     return v;
148 }
149 
150 /// Computes sin(x)/x accurately.
151 /// See: http://www.plunk.org/~hatch/rightway.php
152 T sinOverX(T)(T x)
153 {
154     if (1 + x * x == 1)
155         return 1;
156     else
157         return sin(x) / x;
158 }
159 
160 
161 /// Signed integer modulo a/b where the remainder is guaranteed to be in [0..b[,
162 /// even if a is negative. Only support positive dividers.
163 T moduloWrap(T)(T a, T b) pure nothrow if (isSigned!T)
164 in
165 {
166     assert(b > 0);
167 }
168 body
169 {
170     if (a >= 0)
171         a = a % b;
172     else
173     {
174         auto rem = a % b;
175         x = (rem == 0) ? 0 : (-rem + b);
176     }
177 
178     assert(x >= 0 && x < b);
179     return x;
180 }
181 
182 unittest
183 {
184     assert(nextPowerOf2(13) == 16);
185 }
186 
187 /**
188  * Find the root of a linear polynomial a + b x = 0
189  * Returns: Number of roots.
190  */
191 size_t solveLinear(T)(T a, T b, out T root) pure nothrow if (isFloatingPoint!T)
192 {
193     if (b == 0)
194     {
195         return 0;
196     }
197     else
198     {
199         root = -a / b;
200         return 1;
201     }
202 }
203 
204 
205 /**
206  * Finds the root roots of a quadratic polynomial a + b x + c x^2 = 0
207  * Params:
208  *     outRoots = array of root results, should have room for at least 2 elements.
209  * Returns: Number of roots in outRoots.
210  */
211 size_t solveQuadratic(T)(T a, T b, T c, T[] outRoots) pure nothrow if (isFloatingPoint!T)
212 {
213     assert(outRoots.length >= 2);
214     if (c == 0)
215         return solveLinear(a, b, outRoots[0]);
216 
217     T delta = b * b - 4 * a * c;
218     if (delta < 0.0 )
219         return 0;
220 
221     delta = sqrt(delta);
222     T oneOver2a = 0.5 / a;
223 
224     outRoots[0] = oneOver2a * (-b - delta);
225     outRoots[1] = oneOver2a * (-b + delta);
226     return 2;
227 }
228 
229 
230 /**
231  * Finds the roots of a cubic polynomial  a + b x + c x^2 + d x^3 = 0
232  * Params:
233  *     outRoots = array of root results, should have room for at least 2 elements.
234  * Returns: Number of roots in outRoots.
235  * See_also: $(WEB www.codeguru.com/forum/archive/index.php/t-265551.html)
236  */
237 size_t solveCubic(T)(T a, T b, T c, T d, T[] outRoots) pure nothrow if (isFloatingPoint!T)
238 {
239     assert(outRoots.length >= 3);
240     if (d == 0)
241         return solveQuadratic(a, b, c, outRoots);
242 
243     // adjust coefficients
244     T a1 = c / d,
245       a2 = b / d,
246       a3 = a / d;
247 
248     T Q = (a1 * a1 - 3 * a2) / 9,
249       R = (2 * a1 * a1 * a1 - 9 * a1 * a2 + 27 * a3) / 54;
250 
251     T Qcubed = Q * Q * Q;
252     T d2 = Qcubed - R * R;
253 
254     if (d2 >= 0)
255     {
256         // 3 real roots
257         if (Q < 0.0)
258             return 0;
259         T P = R / sqrt(Qcubed);
260 
261         assert(-1 <= P && P <= 1);
262         T theta = acos(P);
263         T sqrtQ = sqrt(Q);
264 
265         outRoots[0] = -2 * sqrtQ * cos(theta / 3) - a1 / 3;
266         outRoots[1] = -2 * sqrtQ * cos((theta + 2 * PI) / 3) - a1 / 3;
267         outRoots[2] = -2 * sqrtQ * cos((theta + 4 * PI) / 3) - a1 / 3;
268         return 3;
269     }
270     else
271     {
272         // 1 real root
273         T e = (sqrt(-d) + abs(R)) ^^ cast(T)(1.0 / 3.0);
274         if (R > 0)
275             e = -e;
276         outRoots[0] = e + Q / e - a1 / 3.0;
277         return 1;
278     }
279 }
280 
281 /**
282  * Returns the roots of a quartic polynomial  a + b x + c x^2 + d x^3 + e x^4 = 0
283  *
284  * Returns number of roots. roots slice should have room for up to 4 elements.
285  * Bugs: doesn't pass unit-test!
286  * See_also: $(WEB mathworld.wolfram.com/QuarticEquation.html)
287  */
288 size_t solveQuartic(T)(T a, T b, T c, T d, T e, T[] roots) pure nothrow if (isFloatingPoint!T)
289 {
290     assert(roots.length >= 4);
291 
292     if (e == 0)
293         return solveCubic(a, b, c, d, roots);
294 
295     // Adjust coefficients
296     T a0 = a / e,
297       a1 = b / e,
298       a2 = c / e,
299       a3 = d / e;
300 
301     // Find a root for the following cubic equation:
302     //     y^3 - a2 y^2 + (a1 a3 - 4 a0) y + (4 a2 a0 - a1 ^2 - a3^2 a0) = 0
303     // aka Resolvent cubic
304     T b0 = 4 * a2 * a0 - a1 * a1 - a3 * a3 * a0;
305     T b1 = a1 * a3 - 4 * a0;
306     T b2 = -a2;
307     T[3] resolventCubicRoots;
308     size_t numRoots = solveCubic!T(b0, b1, b2, 1, resolventCubicRoots[]);
309     assert(numRoots == 3);
310     T y = resolventCubicRoots[0];
311     if (y < resolventCubicRoots[1]) y = resolventCubicRoots[1];
312     if (y < resolventCubicRoots[2]) y = resolventCubicRoots[2];
313 
314     // Compute R, D & E
315     T R = 0.25f * a3 * a3 - a2 + y;
316     if (R < 0.0)
317         return 0;
318     R = sqrt(R);
319 
320     T D = void,
321       E = void;
322     if (R == 0)
323     {
324         T d1 = 0.75f * a3 * a3 - 2 * a2;
325         T d2 = 2 * sqrt(y * y - 4 * a0);
326         D = sqrt(d1 + d2) * 0.5f;
327         E = sqrt(d1 - d2) * 0.5f;
328     }
329     else
330     {
331         T Rsquare = R * R;
332         T Rrec = 1 / R;
333         T d1 =  0.75f * a3 * a3 - Rsquare - 2 * a2;
334         T d2 = 0.25f * Rrec * (4 * a3 * a2 - 8 * a1 - a3 * a3 * a3);
335         D = sqrt(d1 + d2) * 0.5f;
336         E = sqrt(d1 - d2) * 0.5f;
337     }
338 
339     // Compute the 4 roots
340     a3 *= -0.25f;
341     R *= 0.5f;
342 
343     roots[0] = a3 + R + D;
344     roots[1] = a3 + R - D;
345     roots[2] = a3 - R + E;
346     roots[3] = a3 - R - E;
347     return 4;
348 }
349 
350 
351 unittest
352 {
353     bool arrayContainsRoot(double[] arr, double root)
354     {
355         foreach(e; arr)
356             if (abs(e - root) < 1e-7)
357                 return true;
358         return false;
359     }
360 
361     // test quadratic
362     {
363         double[3] roots;
364         size_t numRoots = solveCubic!double(-2, -3 / 2.0, 3 / 4.0, 1 / 4.0, roots[]);
365         assert(numRoots == 3);
366         assert(arrayContainsRoot(roots[], -4));
367         assert(arrayContainsRoot(roots[], -1));
368         assert(arrayContainsRoot(roots[], 2));
369     }
370 
371     // test quartic
372     {
373         double[4] roots;
374         size_t numRoots = solveQuartic!double(0, -2, -1, 2, 1, roots[]);
375 
376         assert(numRoots == 4);
377         assert(arrayContainsRoot(roots[], -2));
378         assert(arrayContainsRoot(roots[], -1));
379         assert(arrayContainsRoot(roots[], 0));
380         assert(arrayContainsRoot(roots[], 1));
381     }
382 }