1 /**
2  * Contains fast approximations of some {@link java.lang.Math} operations.
3  * <p>
4  * By default, {@link java.lang.Math} methods will be used by all other DOML classes. In order to use the approximations in this class, start the JVM with the parameter <code>-DDOML.fastmath</code>.
5  * <p>
6  * There are two algorithms for approximating sin/cos:
7  * <ol>
8  * <li>arithmetic <a href="http://www.java-gaming.org/topics/DOML-1-8-0-release/37491/msg/361815/view.html#msg361815">polynomial approximation</a> contributed by roquendm 
9  * <li>theagentd's <a href="http://www.java-gaming.org/topics/extremely-fast-sine-cosine/36469/msg/346213/view.html#msg346213">linear interpolation</a> variant of Riven's algorithm from
10  * <a href="http://www.java-gaming.org/topics/extremely-fast-sine-cosine/36469/view.html">http://www.java-gaming.org/</a>
11  * </ol>
12  * By default, the first algorithm is being used. In order to use the second one, start the JVM with <code>-DDOML.sinLookup</code>. The lookup table bit length of the second algorithm can also be adjusted
13  * for improved accuracy via <code>-DDOML.sinLookup.bits=&lt;n&gt;</code>, where &lt;n&gt; is the number of bits of the lookup table.
14  * 
15  * @author Kai Burjack
16  */
17 module doml.math;
18 
19 import doml.options;
20 
21 import std.traits: Select, isFloatingPoint, isIntegral;
22 
23 import std.math: 
24 math_pi    = PI, 
25 math_sin   = sin, 
26 math_rint  = rint, 
27 math_floor = floor, 
28 math_cos   = cos,
29 math_acos  = acos, 
30 math_sqrt  = sqrt, 
31 math_atan2 = atan2, 
32 math_tan   = tan, 
33 math_asin  = asin, 
34 math_abs   = abs,
35 math_ceil  = ceil, 
36 math_round = round, 
37 math_exp   = exp, 
38 math_fma   = fma,
39 math_pow   = pow,
40 math_isInfinite = isInfinity;
41 
42 // These aren't math library but oh well
43 import std.algorithm: 
44 math_min   = min,
45 math_max   = max;
46 
47 // I'm not indent matching this
48 import std.random: 
49 math_random = Random,
50 math_unpredictableSeed = unpredictableSeed,
51 math_uniform = uniform;
52 
53 import doml.rounding_mode;
54 
55 // All the java comments are now wrong oh nooo
56 
57 
58 /*
59  * The MIT License
60  *
61  * Copyright (c) 2015-2021 DOML
62  +*%#$%# Translated by jordan4ibanez
63  *
64  * Permission is hereby granted, free of charge, to any person obtaining a copy
65  * of this software and associated documentation files (the "Software"), to deal
66  * in the Software without restriction, including without limitation the rights
67  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
68  * copies of the Software, and to permit persons to whom the Software is
69  * furnished to do so, subject to the following conditions:
70  *
71  * The above copyright notice and this permission notice shall be included in
72  * all copies or substantial portions of the Software.
73  *
74  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
75  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
76  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
77  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
78  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
79  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
80  * THE SOFTWARE.
81  */
82 
83 /*
84 * The following implementation of an approximation of sine and cosine was
85 * thankfully donated by Riven from http://java-gaming.org/.
86 * 
87 * The code for linear interpolation was gratefully donated by theagentd
88 * from the same site.
89 */
90 
91 immutable double PI = math_pi;
92 immutable double PI2 = PI * 2.0;
93 immutable double PIHalf = PI * 0.5;
94 immutable double PI_4 = PI * 0.25;
95 immutable double PI_INV = 1.0 / PI;
96 
97 
98 // Credit: https://forum.dlang.org/post/bug-5900-3@http.d.puremagic.com%2Fissues%2F
99 /// Converts from degrees to radians.
100 @safe pure nothrow  Select!(isFloatingPoint!T || isIntegral!T, T, double)
101 radians(T)(in T x) {
102     return x * (PI / 180);
103 }
104 
105 /// Converts from radians to degrees.
106 @safe pure nothrow Select!(isFloatingPoint!T || isIntegral!T, T, double)
107 degrees(T)(in T x) {
108     return x / (PI / 180);
109 }
110 
111 @safe pure nothrow Select!(isFloatingPoint!T || isIntegral!T, T, double)
112 math_signum(T)(in T x) {
113     return (T(0) < x) - (x < T(0));
114 }
115 
116 // Thanks to adr, a Java function in D
117 double longbits(long a) {
118     return *cast(double*) &a;
119 }
120 int doubleToRawIntBits(double a) {
121     return *cast(int*) &a;
122 }
123 long doubleToLongBits(double a) {
124     return *cast(long*) &a;
125 }
126 
127 bool isInfinite(double value) {
128     return math_isInfinite(value);
129 }
130 
131 
132 private const double c1 = longbits(-4_628_199_217_061_079_772L);
133 private const double c2 = longbits(4_575_957_461_383_582_011L);
134 private const double c3 = longbits(-4_671_919_876_300_759_001L);
135 private const double c4 = longbits(4_523_617_214_285_661_942L);
136 private const double c5 = longbits(-4_730_215_272_828_025_532L);
137 private const double c6 = longbits(4_460_272_573_143_870_633L);
138 private const double c7 = longbits(-4_797_767_418_267_846_529L);
139 
140 /**
141 * @author theagentd
142 */
143 double sin_theagentd_arith(double x){
144     double xi = math_floor((x + PI_4) * PI_INV);
145     double x_ = x - xi * PI;
146     double sign = (cast(int)xi & 1) * -2 + 1;
147     double x2 = x_ * x_;
148     double sin = x_;
149     double tx = x_ * x2;
150     sin += tx * c1; tx *= x2;
151     sin += tx * c2; tx *= x2;
152     sin += tx * c3; tx *= x2;
153     sin += tx * c4; tx *= x2;
154     sin += tx * c5; tx *= x2;
155     sin += tx * c6; tx *= x2;
156     sin += tx * c7;
157     return sign * sin;
158 }
159 
160 /**
161     * Reference: <a href="http://www.java-gaming.org/topics/DOML-1-8-0-release/37491/msg/361718/view.html#msg361718">http://www.java-gaming.org/</a>
162     */
163 double sin_roquen_arith(double x) {
164     double xi = math_floor((x + PI_4) * PI_INV);
165     double x_ = x - xi * PI;
166     double sign = (cast(int)xi & 1) * -2 + 1;
167     double x2 = x_ * x_;
168 
169     // code from sin_theagentd_arith:
170     // double sin = x_;
171     // double tx = x_ * x2;
172     // sin += tx * c1; tx *= x2;
173     // sin += tx * c2; tx *= x2;
174     // sin += tx * c3; tx *= x2;
175     // sin += tx * c4; tx *= x2;
176     // sin += tx * c5; tx *= x2;
177     // sin += tx * c6; tx *= x2;
178     // sin += tx * c7;
179     // return sign * sin;
180 
181     double sin;
182     x_  = sign*x_;
183     sin =          c7;
184     sin = sin*x2 + c6;
185     sin = sin*x2 + c5;
186     sin = sin*x2 + c4;
187     sin = sin*x2 + c3;
188     sin = sin*x2 + c2;
189     sin = sin*x2 + c1;
190     return x_ + x_*x2*sin;
191 }
192 
193 private const double s5 = longbits(4_523_227_044_276_562_163L);
194 private const double s4 = longbits(-4_671_934_770_969_572_232L);
195 private const double s3 = longbits(4_575_957_211_482_072_852L);
196 private const double s2 = longbits(-4_628_199_223_918_090_387L);
197 private const double s1 = longbits(4_607_182_418_589_157_889L);
198 
199 /**
200 * Reference: <a href="http://www.java-gaming.org/topics/DOML-1-8-0-release/37491/msg/361815/view.html#msg361815">http://www.java-gaming.org/</a>
201 */
202 double sin_roquen_9(double v) {
203     double i  = math_rint(v*PI_INV);
204     double x  = v - i * PI;
205     double qs = 1-2*(cast(int)i & 1);
206     double x2 = x*x;
207     double r;
208     x = qs*x;
209     r =        s5;
210     r = r*x2 + s4;
211     r = r*x2 + s3;
212     r = r*x2 + s2;
213     r = r*x2 + s1;
214     return x*r;
215 }
216 
217 private const double k1 = longbits(-4_628_199_217_061_079_959L);
218 private const double k2 = longbits(4_575_957_461_383_549_981L);
219 private const double k3 = longbits(-4_671_919_876_307_284_301L);
220 private const double k4 = longbits(4_523_617_213_632_129_738L);
221 private const double k5 = longbits(-4_730_215_344_060_517_252L);
222 private const double k6 = longbits(4_460_268_259_291_226_124L);
223 private const double k7 = longbits(-4_798_040_743_777_455_072L);
224 
225 /**
226 * Reference: <a href="http://www.java-gaming.org/topics/DOML-1-8-0-release/37491/msg/361815/view.html#msg361815">http://www.java-gaming.org/</a>
227 */
228 double sin_roquen_newk(double v) {
229     double i  = math_rint(v*PI_INV);
230     double x  = v - i * PI;
231     double qs = 1-2*(cast(int)i & 1);
232     double x2 = x*x;
233     double r;
234     x = qs*x;
235     r =        k7;
236     r = r*x2 + k6;
237     r = r*x2 + k5;
238     r = r*x2 + k4;
239     r = r*x2 + k3;
240     r = r*x2 + k2;
241     r = r*x2 + k1;
242     return x + x*x2*r;
243 }
244 
245 double pow(double a, double b) {
246     return math_pow(a, b);
247 }
248 
249 double sin(double rad) {
250     return math_sin(rad);
251 }
252 
253 double cos(double rad) {
254     return math_cos(rad);
255 }
256 
257 double cosFromSin(double sin, double angle) {
258     //if (Options.FASTMATH){
259         // return math_sin(angle + PIHalf);
260     // }
261     // sin(x)^2 + cos(x)^2 = 1
262     double cos = math_sqrt(1.0 - sin * sin);
263     double a = angle + PIHalf;
264     double b = a - cast(int)(a / PI2) * PI2;
265     if (b < 0.0)
266         b = PI2 + b;
267     if (b >= PI)
268         return -cos;
269     return cos;
270 }
271 
272 /* Other math functions not yet approximated */
273 
274 double sqrt(double r) {
275     return math_sqrt(r);
276 }
277 
278 double invsqrt(double r) {
279     return 1.0 / math_sqrt(r);
280 }
281 
282 double tan(double r) {
283     return math_tan(r);
284 }
285 
286 double acos(double r) {
287     return math_acos(r);
288 }
289 
290 double safeAcos(double v) {
291     if (v < -1.0)
292         return PI;
293     else if (v > +1.0)
294         return 0.0;
295     else
296         return math_acos(v);
297 }
298 
299 
300 double atan2(double y, double x) {
301     return math_atan2(y, x);
302 }
303 
304 double asin(double r) {
305     return math_asin(r);
306 }
307 
308 double abs(double r) {
309     return math_abs(r);
310 }
311 
312 // Taken from runtime
313 bool equals(double a, double b, double delta) {
314     return doubleToLongBits(a) == doubleToLongBits(b) || abs(a - b) <= delta;
315 }
316 
317 bool absEqualsOne(double r) {
318     return (doubleToLongBits(r) & 0x7FFFFFFFFFFFFFFFL) == 0x3FF0000000000000L;
319 }
320 
321 int abs(int r) {
322     return math_abs(r);
323 }
324 
325 int max(int x, int y) {
326     return math_max(x, y);
327 }
328 
329 int min(int x, int y) {
330     return math_min(x, y);
331 }
332 
333 double min(double a, double b) {
334     return a < b ? a : b;
335 }
336 
337 double max(double a, double b) {
338     return a > b ? a : b;
339 }
340 
341 ulong max(ulong a, ulong b) {
342     return a > b ? a : b;
343 }
344 
345 double clamp(double a, double b, double val) {
346     return max(a,math_min(b,val));
347 }
348 int clamp(int a, int b, int val) {
349     return max(a, math_min(b, val));
350 }
351 
352 double toRadians(double angles) {
353     return radians(angles);
354 }
355 
356 double toDegrees(double angles) {
357     return degrees(angles);
358 }
359 
360 double floor(double v) {
361     return math_floor(v);
362 }
363 
364 double ceil(double v) {
365     return math_ceil(v);
366 }
367 
368 long round(double v) {
369     return cast(long) math_round(v);
370 }
371 
372 double exp(double a) {
373     return math_exp(a);
374 }
375 
376 bool isFinite(double d) {
377     return math_abs(d) <= double.max;
378 }
379 
380 double fma(double a, double b, double c) {
381     return math_fma(a, b, c);
382 }
383 
384 int roundUsing(double v, int mode) {
385     switch (mode) {
386         case RoundingMode.TRUNCATE:
387             return cast(int) v;
388         case RoundingMode.CEILING:
389             return cast(int) math_ceil(v);
390         case RoundingMode.FLOOR:
391             return cast(int) math_floor(v);
392         case RoundingMode.HALF_DOWN:
393             return roundHalfDown(v);
394         case RoundingMode.HALF_UP:
395             return roundHalfUp(v);
396         case RoundingMode.HALF_EVEN:
397             return roundHalfEven(v);
398         default: {
399             return 0;
400         }
401     }
402 }
403 
404 double lerp(double a, double b, double t) {
405     return math_fma(b - a, t, a);
406 }
407 
408 double biLerp(double q00, double q10, double q01, double q11, double tx, double ty) {
409     double lerpX1 = lerp(q00, q10, tx);
410     double lerpX2 = lerp(q01, q11, tx);
411     return lerp(lerpX1, lerpX2, ty);
412 }
413 
414 double triLerp(double q000, double q100, double q010, double q110, double q001, double q101, double q011, double q111,
415  double tx, double ty, double tz) {
416     double x00 = lerp(q000, q100, tx);
417     double x10 = lerp(q010, q110, tx);
418     double x01 = lerp(q001, q101, tx);
419     double x11 = lerp(q011, q111, tx);
420     double y0 = lerp(x00, x10, ty);
421     double y1 = lerp(x01, x11, ty);
422     return lerp(y0, y1, tz);
423 }
424 
425 int roundHalfEven(double v) {
426     return cast(int) math_rint(v);
427 }
428 int roundHalfDown(double v) {
429     return (v > 0) ? cast(int) math_ceil(v - 0.5) : cast(int) math_floor(v + 0.5);
430 }
431 int roundHalfUp(double v) {
432     return (v > 0) ? cast(int) math_floor(v + 0.5) : cast(int) math_ceil(v - 0.5);
433 }
434 
435 double random() {
436     math_random random = math_random(math_unpredictableSeed());
437     return math_uniform(0.0, 1.0, random);
438 }
439 
440 double safeAsin(double r) {
441     return r <= -1.0 ? -PIHalf : r >= 1.0 ? PIHalf : math_asin(r);
442 }
443 
444 double signum(double v) {
445     return math_signum(v);
446 }
447 int signum(int v) {
448     int r;
449     r = math_signum(v);
450     r = (v >> 31) | (-v >>> 31);
451     return r;
452 }
453 int signum(long v) {
454     int r;
455     r = cast(int)math_signum(v);
456     r = cast(int) ((v >> 63) | (-v >>> 63));
457     return r;
458 }
459