1 /** 2 This module implements functions for the "Math" namespace in the scripting language. 3 See https://pillager86.github.io/dmildew/Math.html 4 5 ──────────────────────────────────────────────────────────────────────────────── 6 7 Copyright (C) 2021 pillager86.rf.gd 8 9 This program is free software: you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation, either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT ANY 15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 16 PARTICULAR PURPOSE. See the GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 module mildew.stdlib.math; 22 23 import math=std.math; 24 25 import mildew.environment; 26 import mildew.interpreter; 27 import mildew.types; 28 29 /** 30 * Initializes the math library. This is called by Interpreter.initializeStdlib. Functions 31 * are stored in the global Math object and are accessed such as "Math.acos". Documentation 32 * for this library can be found at https://pillager86.github.io/dmildew/Math.html 33 * Params: 34 * interpreter = The Interpreter object to load the namespace into. 35 */ 36 public void initializeMathLibrary(Interpreter interpreter) 37 { 38 // TODO rewrite this mess with mixins 39 ScriptObject mathNamespace = new ScriptObject("Math", null, null); 40 // static members 41 mathNamespace["E"] = ScriptAny(cast(double)math.E); 42 mathNamespace["LN10"] = ScriptAny(cast(double)math.LN10); 43 mathNamespace["LN2"] = ScriptAny(cast(double)math.LN2); 44 mathNamespace["LOG10E"] = ScriptAny(cast(double)math.LOG10E); 45 mathNamespace["LOG2E"] = ScriptAny(cast(double)math.LOG2E); 46 mathNamespace["PI"] = ScriptAny(cast(double)math.PI); 47 mathNamespace["SQRT1_2"] = ScriptAny(cast(double)math.SQRT1_2); 48 mathNamespace["SQRT2"] = ScriptAny(cast(double)math.SQRT2); 49 // functions 50 mathNamespace["abs"] = ScriptAny(new ScriptFunction("Math.abs", &native_Math_abs)); 51 mathNamespace["acos"] = ScriptAny(new ScriptFunction("Math.acos", &native_Math_acos)); 52 mathNamespace["acosh"] = ScriptAny(new ScriptFunction("Math.acosh", &native_Math_acosh)); 53 mathNamespace["asin"] = ScriptAny(new ScriptFunction("Math.asin", &native_Math_asin)); 54 mathNamespace["asinh"] = ScriptAny(new ScriptFunction("Math.asinh", &native_Math_asinh)); 55 mathNamespace["atan"] = ScriptAny(new ScriptFunction("Math.atan", &native_Math_atan)); 56 mathNamespace["atan2"] = ScriptAny(new ScriptFunction("Math.atan2", &native_Math_atan2)); 57 mathNamespace["cbrt"] = ScriptAny(new ScriptFunction("Math.cbrt", &native_Math_cbrt)); 58 mathNamespace["ceil"] = ScriptAny(new ScriptFunction("Math.ceil", &native_Math_ceil)); 59 mathNamespace["clz32"] = ScriptAny(new ScriptFunction("Math.clz32", &native_Math_clz32)); 60 mathNamespace["cos"] = ScriptAny(new ScriptFunction("Math.cos", &native_Math_cos)); 61 mathNamespace["cosh"] = ScriptAny(new ScriptFunction("Math.cosh", &native_Math_cosh)); 62 mathNamespace["exp"] = ScriptAny(new ScriptFunction("Math.exp", &native_Math_exp)); 63 mathNamespace["expm1"] = ScriptAny(new ScriptFunction("Math.expm1", &native_Math_expm1)); 64 mathNamespace["floor"] = ScriptAny(new ScriptFunction("Math.floor", &native_Math_floor)); 65 mathNamespace["fround"] = ScriptAny(new ScriptFunction("Math.fround", &native_Math_fround)); 66 mathNamespace["hypot"] = ScriptAny(new ScriptFunction("Math.hypot", &native_Math_hypot)); 67 mathNamespace["imul"] = ScriptAny(new ScriptFunction("Math.imul", &native_Math_imul)); 68 mathNamespace["log"] = ScriptAny(new ScriptFunction("Math.log", &native_Math_log)); 69 mathNamespace["log10"] = ScriptAny(new ScriptFunction("Math.log10", &native_Math_log10)); 70 mathNamespace["log1p"] = ScriptAny(new ScriptFunction("Math.log1p", &native_Math_log1p)); 71 mathNamespace["log2"] = ScriptAny(new ScriptFunction("Math.log2", &native_Math_log2)); 72 mathNamespace["max"] = ScriptAny(new ScriptFunction("Math.max", &native_Math_max)); 73 mathNamespace["min"] = ScriptAny(new ScriptFunction("Math.min", &native_Math_min)); 74 mathNamespace["pow"] = ScriptAny(new ScriptFunction("Math.pow", &native_Math_pow)); 75 mathNamespace["random"] = ScriptAny(new ScriptFunction("Math.random", &native_Math_random)); 76 mathNamespace["round"] = ScriptAny(new ScriptFunction("Math.round", &native_Math_round)); 77 mathNamespace["sign"] = ScriptAny(new ScriptFunction("Math.sign", &native_Math_sign)); 78 mathNamespace["sin"] = ScriptAny(new ScriptFunction("Math.sin", &native_Math_sin)); 79 mathNamespace["sinh"] = ScriptAny(new ScriptFunction("Math.sinh", &native_Math_sinh)); 80 mathNamespace["sqrt"] = ScriptAny(new ScriptFunction("Math.sqrt", &native_Math_sqrt)); 81 mathNamespace["tan"] = ScriptAny(new ScriptFunction("Math.tan", &native_Math_tan)); 82 mathNamespace["tanh"] = ScriptAny(new ScriptFunction("Math.tanh", &native_Math_tanh)); 83 mathNamespace["trunc"] = ScriptAny(new ScriptFunction("Math.trunc", &native_Math_trunc)); 84 interpreter.forceSetGlobal("Math", mathNamespace, true); 85 } 86 87 // TODO rewrite half of this mess with mixins 88 89 private ScriptAny native_Math_abs(Environment env, 90 ScriptAny* thisObj, 91 ScriptAny[] args, 92 ref NativeFunctionError nfe) 93 { 94 if(args.length < 1) 95 return ScriptAny(double.nan); 96 if(!args[0].isNumber) 97 return ScriptAny(double.nan); 98 if(args[0].type == ScriptAny.Type.INTEGER) 99 return ScriptAny(math.abs(args[0].toValue!long)); 100 return ScriptAny(math.abs(args[0].toValue!double)); 101 } 102 103 private ScriptAny native_Math_acos(Environment env, 104 ScriptAny* thisObj, 105 ScriptAny[] args, 106 ref NativeFunctionError nfe) 107 { 108 if(args.length < 1) 109 return ScriptAny(double.nan); 110 if(!args[0].isNumber) 111 return ScriptAny(double.nan); 112 return ScriptAny(math.acos(args[0].toValue!double)); 113 } 114 115 private ScriptAny native_Math_acosh(Environment env, 116 ScriptAny* thisObj, 117 ScriptAny[] args, 118 ref NativeFunctionError nfe) 119 { 120 if(args.length < 1) 121 return ScriptAny(double.nan); 122 if(!args[0].isNumber) 123 return ScriptAny(double.nan); 124 return ScriptAny(math.acosh(args[0].toValue!double)); 125 } 126 127 private ScriptAny native_Math_asin(Environment env, 128 ScriptAny* thisObj, 129 ScriptAny[] args, 130 ref NativeFunctionError nfe) 131 { 132 if(args.length < 1) 133 return ScriptAny(double.nan); 134 if(!args[0].isNumber) 135 return ScriptAny(double.nan); 136 return ScriptAny(math.asin(args[0].toValue!double)); 137 } 138 139 private ScriptAny native_Math_asinh(Environment env, 140 ScriptAny* thisObj, 141 ScriptAny[] args, 142 ref NativeFunctionError nfe) 143 { 144 if(args.length < 1) 145 return ScriptAny(double.nan); 146 if(!args[0].isNumber) 147 return ScriptAny(double.nan); 148 return ScriptAny(math.asinh(args[0].toValue!double)); 149 } 150 151 private ScriptAny native_Math_atan(Environment env, 152 ScriptAny* thisObj, 153 ScriptAny[] args, 154 ref NativeFunctionError nfe) 155 { 156 if(args.length < 1) 157 return ScriptAny(double.nan); 158 if(!args[0].isNumber) 159 return ScriptAny(double.nan); 160 return ScriptAny(math.atan(args[0].toValue!double)); 161 } 162 163 private ScriptAny native_Math_atan2(Environment env, 164 ScriptAny* thisObj, 165 ScriptAny[] args, 166 ref NativeFunctionError nfe) 167 { 168 if(args.length < 2) 169 return ScriptAny(double.nan); 170 if(!args[0].isNumber || !args[1].isNumber) 171 return ScriptAny(double.nan); 172 return ScriptAny(math.atan2(args[0].toValue!double, args[1].toValue!double)); 173 } 174 175 private ScriptAny native_Math_cbrt(Environment env, 176 ScriptAny* thisObj, 177 ScriptAny[] args, 178 ref NativeFunctionError nfe) 179 { 180 if(args.length < 1) 181 return ScriptAny(double.nan); 182 if(!args[0].isNumber) 183 return ScriptAny(double.nan); 184 return ScriptAny(math.cbrt(args[0].toValue!double)); 185 } 186 187 private ScriptAny native_Math_ceil(Environment env, 188 ScriptAny* thisObj, 189 ScriptAny[] args, 190 ref NativeFunctionError nfe) 191 { 192 if(args.length < 1) 193 return ScriptAny(double.nan); 194 if(!args[0].isNumber) 195 return ScriptAny(double.nan); 196 if(args[0].isInteger) 197 return ScriptAny(args[0].toValue!long); 198 return ScriptAny(cast(long)math.ceil(args[0].toValue!double)); 199 } 200 201 private ScriptAny native_Math_clz32(Environment env, 202 ScriptAny* thisObj, 203 ScriptAny[] args, 204 ref NativeFunctionError nfe) 205 { 206 if(args.length < 1) 207 return ScriptAny(0); 208 if(!args[0].isNumber) 209 return ScriptAny(0); 210 immutable uint num = args[0].toValue!uint; 211 return ScriptAny(CLZ1(num)); 212 } 213 214 private ScriptAny native_Math_cos(Environment env, 215 ScriptAny* thisObj, 216 ScriptAny[] args, 217 ref NativeFunctionError nfe) 218 { 219 if(args.length < 1) 220 return ScriptAny(double.nan); 221 if(!args[0].isNumber) 222 return ScriptAny(double.nan); 223 return ScriptAny(math.cos(args[0].toValue!double)); 224 } 225 226 private ScriptAny native_Math_cosh(Environment env, 227 ScriptAny* thisObj, 228 ScriptAny[] args, 229 ref NativeFunctionError nfe) 230 { 231 if(args.length < 1) 232 return ScriptAny(double.nan); 233 if(!args[0].isNumber) 234 return ScriptAny(double.nan); 235 return ScriptAny(math.cosh(args[0].toValue!double)); 236 } 237 238 private ScriptAny native_Math_exp(Environment env, 239 ScriptAny* thisObj, 240 ScriptAny[] args, 241 ref NativeFunctionError nfe) 242 { 243 if(args.length < 1) 244 return ScriptAny(double.nan); 245 if(!args[0].isNumber) 246 return ScriptAny(double.nan); 247 return ScriptAny(math.exp(args[0].toValue!double)); 248 } 249 250 private ScriptAny native_Math_expm1(Environment env, 251 ScriptAny* thisObj, 252 ScriptAny[] args, 253 ref NativeFunctionError nfe) 254 { 255 if(args.length < 1) 256 return ScriptAny(double.nan); 257 if(!args[0].isNumber) 258 return ScriptAny(double.nan); 259 return ScriptAny(math.expm1(args[0].toValue!double)); 260 } 261 262 private ScriptAny native_Math_floor(Environment env, 263 ScriptAny* thisObj, 264 ScriptAny[] args, 265 ref NativeFunctionError nfe) 266 { 267 if(args.length < 1) 268 return ScriptAny(double.nan); 269 if(!args[0].isNumber) 270 return ScriptAny(double.nan); 271 if(args[0].isInteger) 272 return ScriptAny(args[0].toValue!long); 273 return ScriptAny(cast(long)math.floor(args[0].toValue!double)); 274 } 275 276 private ScriptAny native_Math_fround(Environment env, 277 ScriptAny* thisObj, 278 ScriptAny[] args, 279 ref NativeFunctionError nfe) 280 { 281 if(args.length < 1) 282 return ScriptAny(double.nan); 283 if(!args[0].isNumber) 284 return ScriptAny(double.nan); 285 immutable float f = args[0].toValue!float; 286 return ScriptAny(f); 287 } 288 289 private ScriptAny native_Math_hypot(Environment env, 290 ScriptAny* thisObj, 291 ScriptAny[] args, 292 ref NativeFunctionError nfe) 293 { 294 double sum = 0; 295 foreach(arg ; args) 296 { 297 if(!arg.isNumber) 298 return ScriptAny(double.nan); 299 sum += arg.toValue!double * arg.toValue!double; 300 } 301 return ScriptAny(math.sqrt(sum)); 302 } 303 304 private ScriptAny native_Math_imul(Environment env, 305 ScriptAny* thisObj, 306 ScriptAny[] args, 307 ref NativeFunctionError nfe) 308 { 309 if(args.length < 2) 310 return ScriptAny(double.nan); 311 if(!args[0].isNumber || !args[1].isNumber) 312 return ScriptAny(double.nan); 313 immutable a = args[0].toValue!int; 314 immutable b = args[1].toValue!int; 315 return ScriptAny(a * b); 316 } 317 318 private ScriptAny native_Math_log(Environment env, 319 ScriptAny* thisObj, 320 ScriptAny[] args, 321 ref NativeFunctionError nfe) 322 { 323 if(args.length < 1) 324 return ScriptAny(double.nan); 325 if(!args[0].isNumber) 326 return ScriptAny(double.nan); 327 return ScriptAny(math.log(args[0].toValue!double)); 328 } 329 330 private ScriptAny native_Math_log10(Environment env, 331 ScriptAny* thisObj, 332 ScriptAny[] args, 333 ref NativeFunctionError nfe) 334 { 335 if(args.length < 1) 336 return ScriptAny(double.nan); 337 if(!args[0].isNumber) 338 return ScriptAny(double.nan); 339 return ScriptAny(math.log10(args[0].toValue!double)); 340 } 341 342 private ScriptAny native_Math_log1p(Environment env, 343 ScriptAny* thisObj, 344 ScriptAny[] args, 345 ref NativeFunctionError nfe) 346 { 347 if(args.length < 1) 348 return ScriptAny(double.nan); 349 if(!args[0].isNumber) 350 return ScriptAny(double.nan); 351 return ScriptAny(math.log1p(args[0].toValue!double)); 352 } 353 354 private ScriptAny native_Math_log2(Environment env, 355 ScriptAny* thisObj, 356 ScriptAny[] args, 357 ref NativeFunctionError nfe) 358 { 359 if(args.length < 1) 360 return ScriptAny(double.nan); 361 if(!args[0].isNumber) 362 return ScriptAny(double.nan); 363 return ScriptAny(math.log2(args[0].toValue!double)); 364 } 365 366 private ScriptAny native_Math_max(Environment env, 367 ScriptAny* thisObj, 368 ScriptAny[] args, 369 ref NativeFunctionError nfe) 370 { 371 import std.algorithm: max; 372 if(args.length < 1) 373 return ScriptAny(double.nan); 374 if(!args[0].isNumber) 375 return ScriptAny(double.nan); 376 double maxNumber = args[0].toValue!double; 377 for(size_t i = 1; i < args.length; ++i) 378 { 379 if(!args[i].isNumber) 380 return ScriptAny.UNDEFINED; 381 immutable temp = args[i].toValue!double; 382 if(temp > maxNumber) 383 maxNumber = temp; 384 } 385 return ScriptAny(maxNumber); 386 } 387 388 private ScriptAny native_Math_min(Environment env, 389 ScriptAny* thisObj, 390 ScriptAny[] args, 391 ref NativeFunctionError nfe) 392 { 393 import std.algorithm: max; 394 if(args.length < 1) 395 return ScriptAny(double.nan); 396 if(!args[0].isNumber) 397 return ScriptAny(double.nan); 398 double minNumber = args[0].toValue!double; 399 for(size_t i = 1; i < args.length; ++i) 400 { 401 if(!args[i].isNumber) 402 return ScriptAny.UNDEFINED; 403 immutable temp = args[i].toValue!double; 404 if(temp < minNumber) 405 minNumber = temp; 406 } 407 return ScriptAny(minNumber); 408 } 409 410 private ScriptAny native_Math_pow(Environment env, 411 ScriptAny* thisObj, 412 ScriptAny[] args, 413 ref NativeFunctionError nfe) 414 { 415 if(args.length < 2) 416 return ScriptAny(double.nan); 417 if(!args[0].isNumber || !args[1].isNumber) 418 return ScriptAny(double.nan); 419 return ScriptAny(math.pow(args[0].toValue!double, args[1].toValue!double)); 420 } 421 422 private ScriptAny native_Math_random(Environment env, 423 ScriptAny* thisObj, 424 ScriptAny[] args, 425 ref NativeFunctionError nfe) 426 { 427 import std.random : uniform; 428 return ScriptAny(uniform(0.0, 1.0)); 429 } 430 431 private ScriptAny native_Math_round(Environment env, 432 ScriptAny* thisObj, 433 ScriptAny[] args, 434 ref NativeFunctionError nfe) 435 { 436 if(args.length < 1) 437 return ScriptAny(double.nan); 438 if(!args[0].isNumber) 439 return ScriptAny(double.nan); 440 if(args[0].isInteger) 441 return ScriptAny(args[0].toValue!long); 442 return ScriptAny(cast(long)math.round(args[0].toValue!double)); 443 } 444 445 private ScriptAny native_Math_sign(Environment env, 446 ScriptAny* thisObj, 447 ScriptAny[] args, 448 ref NativeFunctionError nfe) 449 { 450 if(args.length < 1) 451 return ScriptAny(double.nan); 452 if(!args[0].isNumber) 453 return ScriptAny(double.nan); 454 immutable num = args[0].toValue!double; 455 if(num < 0) 456 return ScriptAny(-1); 457 else if(num > 0) 458 return ScriptAny(1); 459 else 460 return ScriptAny(0); 461 } 462 463 private ScriptAny native_Math_sin(Environment env, 464 ScriptAny* thisObj, 465 ScriptAny[] args, 466 ref NativeFunctionError nfe) 467 { 468 if(args.length < 1) 469 return ScriptAny(double.nan); 470 if(!args[0].isNumber) 471 return ScriptAny(double.nan); 472 return ScriptAny(math.sin(args[0].toValue!double)); 473 } 474 475 private ScriptAny native_Math_sinh(Environment env, 476 ScriptAny* thisObj, 477 ScriptAny[] args, 478 ref NativeFunctionError nfe) 479 { 480 if(args.length < 1) 481 return ScriptAny(double.nan); 482 if(!args[0].isNumber) 483 return ScriptAny(double.nan); 484 return ScriptAny(math.sinh(args[0].toValue!double)); 485 } 486 487 private ScriptAny native_Math_sqrt(Environment env, 488 ScriptAny* thisObj, 489 ScriptAny[] args, 490 ref NativeFunctionError nfe) 491 { 492 if(args.length < 1) 493 return ScriptAny.UNDEFINED; 494 if(!args[0].isNumber) 495 return ScriptAny.UNDEFINED; 496 return ScriptAny(math.sqrt(args[0].toValue!double)); 497 } 498 499 private ScriptAny native_Math_tan(Environment env, 500 ScriptAny* thisObj, 501 ScriptAny[] args, 502 ref NativeFunctionError nfe) 503 { 504 if(args.length < 1) 505 return ScriptAny(double.nan); 506 if(!args[0].isNumber) 507 return ScriptAny(double.nan); 508 return ScriptAny(math.tan(args[0].toValue!double)); 509 } 510 511 private ScriptAny native_Math_tanh(Environment env, 512 ScriptAny* thisObj, 513 ScriptAny[] args, 514 ref NativeFunctionError nfe) 515 { 516 if(args.length < 1) 517 return ScriptAny(double.nan); 518 if(!args[0].isNumber) 519 return ScriptAny(double.nan); 520 return ScriptAny(math.tanh(args[0].toValue!double)); 521 } 522 523 private ScriptAny native_Math_trunc(Environment env, 524 ScriptAny* thisObj, 525 ScriptAny[] args, 526 ref NativeFunctionError nfe) 527 { 528 if(args.length < 1) 529 return ScriptAny(double.nan); 530 if(!args[0].isNumber) 531 return ScriptAny(double.nan); 532 return ScriptAny(math.trunc(args[0].toValue!double)); 533 } 534 535 536 /// software implementation of CLZ32 because I don't know assembly nor care to lock DMildew to a specific CPU 537 /// courtesy of https://embeddedgurus.com/state-space/2014/09/fast-deterministic-and-portable-counting-leading-zeros/ 538 pragma(inline, true) 539 uint CLZ1(uint x) 540 { 541 static immutable ubyte[] clz_lkup = [ 542 32U, 31U, 30U, 30U, 29U, 29U, 29U, 29U, 543 28U, 28U, 28U, 28U, 28U, 28U, 28U, 28U, 544 27U, 27U, 27U, 27U, 27U, 27U, 27U, 27U, 545 27U, 27U, 27U, 27U, 27U, 27U, 27U, 27U, 546 26U, 26U, 26U, 26U, 26U, 26U, 26U, 26U, 547 26U, 26U, 26U, 26U, 26U, 26U, 26U, 26U, 548 26U, 26U, 26U, 26U, 26U, 26U, 26U, 26U, 549 26U, 26U, 26U, 26U, 26U, 26U, 26U, 26U, 550 25U, 25U, 25U, 25U, 25U, 25U, 25U, 25U, 551 25U, 25U, 25U, 25U, 25U, 25U, 25U, 25U, 552 25U, 25U, 25U, 25U, 25U, 25U, 25U, 25U, 553 25U, 25U, 25U, 25U, 25U, 25U, 25U, 25U, 554 25U, 25U, 25U, 25U, 25U, 25U, 25U, 25U, 555 25U, 25U, 25U, 25U, 25U, 25U, 25U, 25U, 556 25U, 25U, 25U, 25U, 25U, 25U, 25U, 25U, 557 25U, 25U, 25U, 25U, 25U, 25U, 25U, 25U, 558 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 559 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 560 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 561 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 562 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 563 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 564 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 565 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 566 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 567 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 568 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 569 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 570 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 571 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 572 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U, 573 24U, 24U, 24U, 24U, 24U, 24U, 24U, 24U 574 ]; 575 uint n; 576 if (x >= (1U << 16)) 577 { 578 if (x >= (1U << 24)) 579 { 580 n = 24U; 581 } 582 else 583 { 584 n = 16U; 585 } 586 } 587 else 588 { 589 if (x >= (1U << 8)) 590 { 591 n = 8U; 592 } 593 else 594 { 595 n = 0U; 596 } 597 } 598 return cast(uint)clz_lkup[x >> n] - n; 599 }