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