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 * Produces a pseudo-random positive integer. 376 * 377 * Returns the calculated pseuo-random value. The value is within the range 378 * `0` to `RAND_MAX` inclusive where `RAND_MAX` is a platform specific value 379 * guaranteed to be at least `32767`. 380 * 381 * The {@link module:math~srand `srand()`} function sets its argument as the 382 * seed for a new sequence of pseudo-random integers to be returned by `rand()`. These sequences are 383 * repeatable by calling {@link module:math~srand `srand()`} with the same 384 * seed value. 385 * 386 * If no seed value is explicitly set by calling 387 * {@link module:math~srand `srand()`} prior to the first call to `rand()`, 388 * the math module will automatically seed the PRNG once, using the current 389 * time of day in milliseconds as seed value. 390 * 391 * @function module:math#rand 392 * 393 * @returns {number} 394 */ 395 static uc_value_t * 396 uc_rand(uc_vm_t *vm, size_t nargs) 397 { 398 struct timeval tv; 399 400 if (!ucv_boolean_get(uc_vm_registry_get(vm, "math.srand_called"))) { 401 gettimeofday(&tv, NULL); 402 srand((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); 403 404 uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(true)); 405 } 406 407 return ucv_int64_new(rand()); 408 } 409 410 /** 411 * Seeds the pseudo-random number generator. 412 * 413 * This functions seeds the PRNG with the given value and thus affects the 414 * pseudo-random integer sequence produced by subsequent calls to 415 * {@link module:math~rand `rand()`}. 416 * 417 * Setting the same seed value will result in the same pseudo-random numbers 418 * produced by {@link module:math~rand `rand()`}. 419 * 420 * @function module:math#srand 421 * 422 * @param {number} seed 423 * The seed value. 424 */ 425 static uc_value_t * 426 uc_srand(uc_vm_t *vm, size_t nargs) 427 { 428 int64_t n = ucv_to_integer(uc_fn_arg(0)); 429 430 srand((unsigned int)n); 431 uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(true)); 432 433 return NULL; 434 } 435 436 /** 437 * Tests whether `x` is a `NaN` double. 438 * 439 * This functions checks whether the given argument is of type `double` with 440 * a `NaN` (not a number) value. 441 * 442 * Returns `true` if the value is `NaN`, otherwise false. 443 * 444 * Note that a value can also be checked for `NaN` with the expression 445 * `x !== x` which only evaluates to `true` if `x` is `NaN`. 446 * 447 * @function module:math#isnan 448 * 449 * @param {number} x 450 * The value to test. 451 * 452 * @returns {boolean} 453 */ 454 static uc_value_t * 455 uc_isnan(uc_vm_t *vm, size_t nargs) 456 { 457 uc_value_t *v = uc_fn_arg(0); 458 459 return ucv_boolean_new(ucv_type(v) == UC_DOUBLE && isnan(ucv_double_get(v))); 460 } 461 462 static const uc_function_list_t math_fns[] = { 463 { "abs", uc_abs }, 464 { "atan2", uc_atan2 }, 465 { "cos", uc_cos }, 466 { "exp", uc_exp }, 467 { "log", uc_log }, 468 { "sin", uc_sin }, 469 { "sqrt", uc_sqrt }, 470 { "pow", uc_pow }, 471 { "rand", uc_rand }, 472 { "srand", uc_srand }, 473 { "isnan", uc_isnan }, 474 }; 475 476 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 477 { 478 uc_function_list_register(scope, math_fns); 479 480 uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(false)); 481 } 482
This page was automatically generated by LXR 0.3.1. • OpenWrt