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