1 /* 2 * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /** 18 * # Mathematical Functions 19 * 20 * The `math` module bundles various mathematical and trigonometrical functions. 21 * 22 * Functions can be individually imported and directly accessed using the 23 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import} 24 * syntax: 25 * 26 * ``` 27 * import { pow, rand } from 'math'; 28 * 29 * let x = pow(2, 5); 30 * let y = rand(); 31 * ``` 32 * 33 * Alternatively, the module namespace can be imported 34 * using a wildcard import statement: 35 * 36 * ``` 37 * import * as math from 'math'; 38 * 39 * let x = math.pow(2, 5); 40 * let y = math.rand(); 41 * ``` 42 * 43 * Additionally, the math module namespace may also be imported by invoking the 44 * `ucode` interpreter with the `-lmath` switch. 45 * 46 * @module math 47 */ 48 49 #include <math.h> 50 #include <errno.h> 51 #include <sys/time.h> 52 53 #include "ucode/module.h" 54 55 56 /** 57 * Returns the absolute value of the given numeric value. 58 * 59 * @function module:math#abs 60 * 61 * @param {*} number 62 * The number to return the absolute value for. 63 * 64 * @returns {number} 65 * Returns the absolute value or `NaN` if the given argument could 66 * not be converted to a number. 67 */ 68 static uc_value_t * 69 uc_abs(uc_vm_t *vm, size_t nargs) 70 { 71 uc_value_t *v = uc_fn_arg(0), *nv, *res; 72 int64_t n; 73 double d; 74 75 nv = v ? ucv_to_number(v) : NULL; 76 77 switch (ucv_type(nv)) { 78 case UC_INTEGER: 79 n = ucv_int64_get(nv); 80 81 if (n >= 0 || errno == ERANGE) 82 res = ucv_get(nv); 83 else if (n == INT64_MIN) 84 res = ucv_uint64_new((uint64_t)INT64_MAX + 1); 85 else 86 res = ucv_uint64_new(-n); 87 88 break; 89 90 case UC_DOUBLE: 91 d = ucv_double_get(nv); 92 93 if (isnan(d) || d >= 0) 94 res = ucv_get(nv); 95 else 96 res = ucv_double_new(-d); 97 98 break; 99 100 default: 101 res = ucv_double_new(NAN); 102 break; 103 } 104 105 ucv_put(nv); 106 107 return res; 108 } 109 110 /** 111 * Calculates the principal value of the arc tangent of `y`/`x`, 112 * using the signs of the two arguments to determine the quadrant 113 * of the result. 114 * 115 * On success, this function returns the principal value of the arc 116 * tangent of `y`/`x` in radians; the return value is in the range [-pi, pi]. 117 * 118 * - If `y` is +0 (-0) and `x` is less than 0, +pi (-pi) is returned. 119 * - If `y` is +0 (-0) and `x` is greater than 0, +0 (-0) is returned. 120 * - If `y` is less than 0 and `x` is +0 or -0, -pi/2 is returned. 121 * - If `y` is greater than 0 and `x` is +0 or -0, pi/2 is returned. 122 * - If either `x` or `y` is NaN, a NaN is returned. 123 * - If `y` is +0 (-0) and `x` is -0, +pi (-pi) is returned. 124 * - If `y` is +0 (-0) and `x` is +0, +0 (-0) is returned. 125 * - If `y` is a finite value greater (less) than 0, and `x` is negative 126 * infinity, +pi (-pi) is returned. 127 * - If `y` is a finite value greater (less) than 0, and `x` is positive 128 * infinity, +0 (-0) is returned. 129 * - If `y` is positive infinity (negative infinity), and `x` is finite, 130 * pi/2 (-pi/2) is returned. 131 * - If `y` is positive infinity (negative infinity) and `x` is negative 132 * infinity, +3*pi/4 (-3*pi/4) is returned. 133 * - If `y` is positive infinity (negative infinity) and `x` is positive 134 * infinity, +pi/4 (-pi/4) is returned. 135 * 136 * When either `x` or `y` can't be converted to a numeric value, `NaN` is 137 * returned. 138 * 139 * @function module:math#atan2 140 * 141 * @param {*} y 142 * The `y` value. 143 * 144 * @param {*} x 145 * The `x` value. 146 * 147 * @returns {number} 148 */ 149 static uc_value_t * 150 uc_atan2(uc_vm_t *vm, size_t nargs) 151 { 152 double d1 = ucv_to_double(uc_fn_arg(0)); 153 double d2 = ucv_to_double(uc_fn_arg(1)); 154 155 if (isnan(d1) || isnan(d2)) 156 return ucv_double_new(NAN); 157 158 return ucv_double_new(atan2(d1, d2)); 159 } 160 161 /** 162 * Calculates the cosine of `x`, where `x` is given in radians. 163 * 164 * Returns the resulting consine value. 165 * 166 * Returns `NaN` if the `x` value can't be converted to a number. 167 * 168 * @function module:math#cos 169 * 170 * @param {number} x 171 * Radians value to calculate cosine for. 172 * 173 * @returns {number} 174 */ 175 static uc_value_t * 176 uc_cos(uc_vm_t *vm, size_t nargs) 177 { 178 double d = ucv_to_double(uc_fn_arg(0)); 179 180 if (isnan(d)) 181 return ucv_double_new(NAN); 182 183 return ucv_double_new(cos(d)); 184 } 185 186 /** 187 * Calculates the value of `e` (the base of natural logarithms) 188 * raised to the power of `x`. 189 * 190 * On success, returns the exponential value of `x`. 191 * 192 * - If `x` is positive infinity, positive infinity is returned. 193 * - If `x` is negative infinity, `+0` is returned. 194 * - If the result underflows, a range error occurs, and zero is returned. 195 * - If the result overflows, a range error occurs, and `Infinity` is returned. 196 * 197 * Returns `NaN` if the `x` value can't be converted to a number. 198 * 199 * @function module:math#exp 200 * 201 * @param {number} x 202 * Power to raise `e` to. 203 * 204 * @returns {number} 205 */ 206 static uc_value_t * 207 uc_exp(uc_vm_t *vm, size_t nargs) 208 { 209 double d = ucv_to_double(uc_fn_arg(0)); 210 211 if (isnan(d)) 212 return ucv_double_new(NAN); 213 214 return ucv_double_new(exp(d)); 215 } 216 217 /** 218 * Calculates the natural logarithm of `x`. 219 * 220 * On success, returns the natural logarithm of `x`. 221 * 222 * - If `x` is `1`, the result is `+0`. 223 * - If `x` is positive nfinity, positive infinity is returned. 224 * - If `x` is zero, then a pole error occurs, and the function 225 * returns negative infinity. 226 * - If `x` is negative (including negative infinity), then a domain 227 * error occurs, and `NaN` is returned. 228 * 229 * Returns `NaN` if the `x` value can't be converted to a number. 230 * 231 * @function module:math#log 232 * 233 * @param {number} x 234 * Value to calulate natural logarithm of. 235 * 236 * @returns {number} 237 */ 238 static uc_value_t * 239 uc_log(uc_vm_t *vm, size_t nargs) 240 { 241 double d = ucv_to_double(uc_fn_arg(0)); 242 243 if (isnan(d)) 244 return ucv_double_new(NAN); 245 246 return ucv_double_new(log(d)); 247 } 248 249 /** 250 * Calculates the sine of `x`, where `x` is given in radians. 251 * 252 * Returns the resulting sine value. 253 * 254 * - When `x` is positive or negative infinity, a domain error occurs 255 * and `NaN` is returned. 256 * 257 * Returns `NaN` if the `x` value can't be converted to a number. 258 * 259 * @function module:math#sin 260 * 261 * @param {number} x 262 * Radians value to calculate sine for. 263 * 264 * @returns {number} 265 */ 266 static uc_value_t * 267 uc_sin(uc_vm_t *vm, size_t nargs) 268 { 269 double d = ucv_to_double(uc_fn_arg(0)); 270 271 if (isnan(d)) 272 return ucv_double_new(NAN); 273 274 return ucv_double_new(sin(d)); 275 } 276 277 /** 278 * Calculates the nonnegative square root of `x`. 279 * 280 * Returns the resulting square root value. 281 * 282 * - If `x` is `+0` (`-0`) then `+0` (`-0`) is returned. 283 * - If `x` is positive infinity, positive infinity is returned. 284 * - If `x` is less than `-0`, a domain error occurs, and `NaN` is returned. 285 * 286 * Returns `NaN` if the `x` value can't be converted to a number. 287 * 288 * @function module:math#sqrt 289 * 290 * @param {number} x 291 * Value to calculate square root for. 292 * 293 * @returns {number} 294 */ 295 static uc_value_t * 296 uc_sqrt(uc_vm_t *vm, size_t nargs) 297 { 298 double d = ucv_to_double(uc_fn_arg(0)); 299 300 if (isnan(d)) 301 return ucv_double_new(NAN); 302 303 return ucv_double_new(sqrt(d)); 304 } 305 306 /** 307 * Calculates the value of `x` raised to the power of `y`. 308 * 309 * On success, returns the value of `x` raised to the power of `y`. 310 * 311 * - If the result overflows, a range error occurs, and the function 312 * returns `Infinity`. 313 * - If result underflows, and is not representable, a range error 314 * occurs, and `0.0` with the appropriate sign is returned. 315 * - If `x` is `+0` or `-0`, and `y` is an odd integer less than `0`, 316 * a pole error occurs `Infinity` is returned, with the same sign 317 * as `x`. 318 * - If `x` is `+0` or `-0`, and `y` is less than `0` and not an odd 319 * integer, a pole error occurs and `Infinity` is returned. 320 * - If `x` is `+0` (`-0`), and `y` is an odd integer greater than `0`, 321 * the result is `+0` (`-0`). 322 * - If `x` is `0`, and `y` greater than `0` and not an odd integer, 323 * the result is `+0`. 324 * - If `x` is `-1`, and `y` is positive infinity or negative infinity, 325 * the result is `1.0`. 326 * - If `x` is `+1`, the result is `1.0` (even if `y` is `NaN`). 327 * - If `y` is `0`, the result is `1.0` (even if `x` is `NaN`). 328 * - If `x` is a finite value less than `0`, and `y` is a finite 329 * noninteger, a domain error occurs, and `NaN` is returned. 330 * - If the absolute value of `x` is less than `1`, and `y` is negative 331 * infinity, the result is positive infinity. 332 * - If the absolute value of `x` is greater than `1`, and `y` is 333 * negative infinity, the result is `+0`. 334 * - If the absolute value of `x` is less than `1`, and `y` is positive 335 * infinity, the result is `+0`. 336 * - If the absolute value of `x` is greater than `1`, and `y` is positive 337 * infinity, the result is positive infinity. 338 * - If `x` is negative infinity, and `y` is an odd integer less than `0`, 339 * the result is `-0`. 340 * - If `x` is negative infinity, and `y` less than `0` and not an odd 341 * integer, the result is `+0`. 342 * - If `x` is negative infinity, and `y` is an odd integer greater than 343 * `0`, the result is negative infinity. 344 * - If `x` is negative infinity, and `y` greater than `0` and not an odd 345 * integer, the result is positive infinity. 346 * - If `x` is positive infinity, and `y` less than `0`, the result is `+0`. 347 * - If `x` is positive infinity, and `y` greater than `0`, the result is 348 * positive infinity. 349 * 350 * Returns `NaN` if either the `x` or `y` value can't be converted to a number. 351 * 352 * @function module:math#pow 353 * 354 * @param {number} x 355 * The base value. 356 * 357 * @param {number} y 358 * The power value. 359 * 360 * @returns {number} 361 */ 362 static uc_value_t * 363 uc_pow(uc_vm_t *vm, size_t nargs) 364 { 365 double x = ucv_to_double(uc_fn_arg(0)); 366 double y = ucv_to_double(uc_fn_arg(1)); 367 368 if (isnan(x) || isnan(y)) 369 return ucv_double_new(NAN); 370 371 return ucv_double_new(pow(x, y)); 372 } 373 374 /** 375 * Depending on the arguments, it produces a pseudo-random positive integer, 376 * or a pseudo-random number in a supplied range. 377 * 378 * Without arguments it returns the calculated pseuo-random value. The value 379 * is within the range `0` to `RAND_MAX` inclusive where `RAND_MAX` is a platform 380 * specific value guaranteed to be at least `32767`. 381 * 382 * With 2 arguments `a, b` it returns a number in the range `a` to `b` inclusive. 383 * With a single argument `a` it returns a number in the range `0` to `a` inclusive. 384 * 385 * The {@link module:math~srand `srand()`} function sets its argument as the 386 * seed for a new sequence of pseudo-random integers to be returned by `rand()`. 387 * These sequences are repeatable by calling {@link module:math~srand `srand()`} 388 * with the same seed value. 389 * 390 * If no seed value is explicitly set by calling 391 * {@link module:math~srand `srand()`} prior to the first call to `rand()`, 392 * the math module will automatically seed the PRNG once, using the current 393 * time of day in milliseconds as seed value. 394 * 395 * @function module:math#rand 396 * 397 * @param {number} [a] 398 * End of the desired range. 399 * 400 * @param {number} [b] 401 * The other end of the desired range. 402 * 403 * @returns {number} 404 */ 405 static uc_value_t * 406 uc_rand(uc_vm_t *vm, size_t nargs) 407 { 408 struct timeval tv; 409 410 if (!ucv_boolean_get(uc_vm_registry_get(vm, "math.srand_called"))) { 411 gettimeofday(&tv, NULL); 412 srand((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); 413 414 uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(true)); 415 } 416 417 if (nargs == 0) 418 return ucv_int64_new(rand()); 419 420 double a = ucv_to_double(uc_fn_arg(0)), b = 0; 421 422 if (nargs > 1) 423 b = ucv_to_double(uc_fn_arg(1)); 424 425 return ucv_double_new(a + ((b - a) * rand()) / RAND_MAX); 426 } 427 428 /** 429 * Seeds the pseudo-random number generator. 430 * 431 * This functions seeds the PRNG with the given value and thus affects the 432 * pseudo-random integer sequence produced by subsequent calls to 433 * {@link module:math~rand `rand()`}. 434 * 435 * Setting the same seed value will result in the same pseudo-random numbers 436 * produced by {@link module:math~rand `rand()`}. 437 * 438 * @function module:math#srand 439 * 440 * @param {number} seed 441 * The seed value. 442 */ 443 static uc_value_t * 444 uc_srand(uc_vm_t *vm, size_t nargs) 445 { 446 int64_t n = ucv_to_integer(uc_fn_arg(0)); 447 448 srand((unsigned int)n); 449 uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(true)); 450 451 return NULL; 452 } 453 454 /** 455 * Tests whether `x` is a `NaN` double. 456 * 457 * This functions checks whether the given argument is of type `double` with 458 * a `NaN` (not a number) value. 459 * 460 * Returns `true` if the value is `NaN`, otherwise false. 461 * 462 * Note that a value can also be checked for `NaN` with the expression 463 * `x !== x` which only evaluates to `true` if `x` is `NaN`. 464 * 465 * @function module:math#isnan 466 * 467 * @param {number} x 468 * The value to test. 469 * 470 * @returns {boolean} 471 */ 472 static uc_value_t * 473 uc_isnan(uc_vm_t *vm, size_t nargs) 474 { 475 uc_value_t *v = uc_fn_arg(0); 476 477 return ucv_boolean_new(ucv_type(v) == UC_DOUBLE && isnan(ucv_double_get(v))); 478 } 479 480 static const uc_function_list_t math_fns[] = { 481 { "abs", uc_abs }, 482 { "atan2", uc_atan2 }, 483 { "cos", uc_cos }, 484 { "exp", uc_exp }, 485 { "log", uc_log }, 486 { "sin", uc_sin }, 487 { "sqrt", uc_sqrt }, 488 { "pow", uc_pow }, 489 { "rand", uc_rand }, 490 { "srand", uc_srand }, 491 { "isnan", uc_isnan }, 492 }; 493 494 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 495 { 496 uc_function_list_register(scope, math_fns); 497 498 uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(false)); 499 } 500
This page was automatically generated by LXR 0.3.1. • OpenWrt