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 static bool srand_called = false; 56 57 /** 58 * Returns the absolute value of the given numeric value. 59 * 60 * @function module:math#abs 61 * 62 * @param {*} number 63 * The number to return the absolute value for. 64 * 65 * @returns {number} 66 * Returns the absolute value or `NaN` if the given argument could 67 * not be converted to a number. 68 */ 69 static uc_value_t * 70 uc_abs(uc_vm_t *vm, size_t nargs) 71 { 72 uc_value_t *v = uc_fn_arg(0), *nv, *res; 73 int64_t n; 74 double d; 75 76 nv = v ? ucv_to_number(v) : NULL; 77 78 switch (ucv_type(nv)) { 79 case UC_INTEGER: 80 n = ucv_int64_get(nv); 81 82 if (n >= 0 || errno == ERANGE) 83 res = ucv_get(nv); 84 else if (n == INT64_MIN) 85 res = ucv_uint64_new((uint64_t)INT64_MAX + 1); 86 else 87 res = ucv_uint64_new(-n); 88 89 break; 90 91 case UC_DOUBLE: 92 d = ucv_double_get(nv); 93 94 if (isnan(d) || d >= 0) 95 res = ucv_get(nv); 96 else 97 res = ucv_double_new(-d); 98 99 break; 100 101 default: 102 res = ucv_double_new(NAN); 103 break; 104 } 105 106 ucv_put(nv); 107 108 return res; 109 } 110 111 /** 112 * Calculates the principal value of the arc tangent of `y`/`x`, 113 * using the signs of the two arguments to determine the quadrant 114 * of the result. 115 * 116 * On success, this function returns the principal value of the arc 117 * tangent of `y`/`x` in radians; the return value is in the range [-pi, pi]. 118 * 119 * - If `y` is +0 (-0) and `x` is less than 0, +pi (-pi) is returned. 120 * - If `y` is +0 (-0) and `x` is greater than 0, +0 (-0) is returned. 121 * - If `y` is less than 0 and `x` is +0 or -0, -pi/2 is returned. 122 * - If `y` is greater than 0 and `x` is +0 or -0, pi/2 is returned. 123 * - If either `x` or `y` is NaN, a NaN is returned. 124 * - If `y` is +0 (-0) and `x` is -0, +pi (-pi) is returned. 125 * - If `y` is +0 (-0) and `x` is +0, +0 (-0) is returned. 126 * - If `y` is a finite value greater (less) than 0, and `x` is negative 127 * infinity, +pi (-pi) is returned. 128 * - If `y` is a finite value greater (less) than 0, and `x` is positive 129 * infinity, +0 (-0) is returned. 130 * - If `y` is positive infinity (negative infinity), and `x` is finite, 131 * pi/2 (-pi/2) is returned. 132 * - If `y` is positive infinity (negative infinity) and `x` is negative 133 * infinity, +3*pi/4 (-3*pi/4) is returned. 134 * - If `y` is positive infinity (negative infinity) and `x` is positive 135 * infinity, +pi/4 (-pi/4) is returned. 136 * 137 * When either `x` or `y` can't be converted to a numeric value, `NaN` is 138 * returned. 139 * 140 * @function module:math#atan2 141 * 142 * @param {*} y 143 * The `y` value. 144 * 145 * @param {*} x 146 * The `x` value. 147 * 148 * @returns {number} 149 */ 150 static uc_value_t * 151 uc_atan2(uc_vm_t *vm, size_t nargs) 152 { 153 double d1 = ucv_to_double(uc_fn_arg(0)); 154 double d2 = ucv_to_double(uc_fn_arg(1)); 155 156 if (isnan(d1) || isnan(d2)) 157 return ucv_double_new(NAN); 158 159 return ucv_double_new(atan2(d1, d2)); 160 } 161 162 /** 163 * Calculates the cosine of `x`, where `x` is given in radians. 164 * 165 * Returns the resulting consine value. 166 * 167 * Returns `NaN` if the `x` value can't be converted to a number. 168 * 169 * @function module:math#cos 170 * 171 * @param {number} x 172 * Radians value to calculate cosine for. 173 * 174 * @returns {number} 175 */ 176 static uc_value_t * 177 uc_cos(uc_vm_t *vm, size_t nargs) 178 { 179 double d = ucv_to_double(uc_fn_arg(0)); 180 181 if (isnan(d)) 182 return ucv_double_new(NAN); 183 184 return ucv_double_new(cos(d)); 185 } 186 187 /** 188 * Calculates the value of `e` (the base of natural logarithms) 189 * raised to the power of `x`. 190 * 191 * On success, returns the exponential value of `x`. 192 * 193 * - If `x` is positive infinity, positive infinity is returned. 194 * - If `x` is negative infinity, `+0` is returned. 195 * - If the result underflows, a range error occurs, and zero is returned. 196 * - If the result overflows, a range error occurs, and `Infinity` is returned. 197 * 198 * Returns `NaN` if the `x` value can't be converted to a number. 199 * 200 * @function module:math#exp 201 * 202 * @param {number} x 203 * Power to raise `e` to. 204 * 205 * @returns {number} 206 */ 207 static uc_value_t * 208 uc_exp(uc_vm_t *vm, size_t nargs) 209 { 210 double d = ucv_to_double(uc_fn_arg(0)); 211 212 if (isnan(d)) 213 return ucv_double_new(NAN); 214 215 return ucv_double_new(exp(d)); 216 } 217 218 /** 219 * Calculates the natural logarithm of `x`. 220 * 221 * On success, returns the natural logarithm of `x`. 222 * 223 * - If `x` is `1`, the result is `+0`. 224 * - If `x` is positive nfinity, positive infinity is returned. 225 * - If `x` is zero, then a pole error occurs, and the function 226 * returns negative infinity. 227 * - If `x` is negative (including negative infinity), then a domain 228 * error occurs, and `NaN` is returned. 229 * 230 * Returns `NaN` if the `x` value can't be converted to a number. 231 * 232 * @function module:math#log 233 * 234 * @param {number} x 235 * Value to calulate natural logarithm of. 236 * 237 * @returns {number} 238 */ 239 static uc_value_t * 240 uc_log(uc_vm_t *vm, size_t nargs) 241 { 242 double d = ucv_to_double(uc_fn_arg(0)); 243 244 if (isnan(d)) 245 return ucv_double_new(NAN); 246 247 return ucv_double_new(log(d)); 248 } 249 250 /** 251 * Calculates the sine of `x`, where `x` is given in radians. 252 * 253 * Returns the resulting sine value. 254 * 255 * - When `x` is positive or negative infinity, a domain error occurs 256 * and `NaN` is returned. 257 * 258 * Returns `NaN` if the `x` value can't be converted to a number. 259 * 260 * @function module:math#sin 261 * 262 * @param {number} x 263 * Radians value to calculate sine for. 264 * 265 * @returns {number} 266 */ 267 static uc_value_t * 268 uc_sin(uc_vm_t *vm, size_t nargs) 269 { 270 double d = ucv_to_double(uc_fn_arg(0)); 271 272 if (isnan(d)) 273 return ucv_double_new(NAN); 274 275 return ucv_double_new(sin(d)); 276 } 277 278 /** 279 * Calculates the nonnegative square root of `x`. 280 * 281 * Returns the resulting square root value. 282 * 283 * - If `x` is `+0` (`-0`) then `+0` (`-0`) is returned. 284 * - If `x` is positive infinity, positive infinity is returned. 285 * - If `x` is less than `-0`, a domain error occurs, and `NaN` is returned. 286 * 287 * Returns `NaN` if the `x` value can't be converted to a number. 288 * 289 * @function module:math#sqrt 290 * 291 * @param {number} x 292 * Value to calculate square root for. 293 * 294 * @returns {number} 295 */ 296 static uc_value_t * 297 uc_sqrt(uc_vm_t *vm, size_t nargs) 298 { 299 double d = ucv_to_double(uc_fn_arg(0)); 300 301 if (isnan(d)) 302 return ucv_double_new(NAN); 303 304 return ucv_double_new(sqrt(d)); 305 } 306 307 /** 308 * Calculates the value of `x` raised to the power of `y`. 309 * 310 * On success, returns the value of `x` raised to the power of `y`. 311 * 312 * - If the result overflows, a range error occurs, and the function 313 * returns `Infinity`. 314 * - If result underflows, and is not representable, a range error 315 * occurs, and `0.0` with the appropriate sign is returned. 316 * - If `x` is `+0` or `-0`, and `y` is an odd integer less than `0`, 317 * a pole error occurs `Infinity` is returned, with the same sign 318 * as `x`. 319 * - If `x` is `+0` or `-0`, and `y` is less than `0` and not an odd 320 * integer, a pole error occurs and `Infinity` is returned. 321 * - If `x` is `+0` (`-0`), and `y` is an odd integer greater than `0`, 322 * the result is `+0` (`-0`). 323 * - If `x` is `0`, and `y` greater than `0` and not an odd integer, 324 * the result is `+0`. 325 * - If `x` is `-1`, and `y` is positive infinity or negative infinity, 326 * the result is `1.0`. 327 * - If `x` is `+1`, the result is `1.0` (even if `y` is `NaN`). 328 * - If `y` is `0`, the result is `1.0` (even if `x` is `NaN`). 329 * - If `x` is a finite value less than `0`, and `y` is a finite 330 * noninteger, a domain error occurs, and `NaN` is returned. 331 * - If the absolute value of `x` is less than `1`, and `y` is negative 332 * infinity, the result is positive infinity. 333 * - If the absolute value of `x` is greater than `1`, and `y` is 334 * negative infinity, the result is `+0`. 335 * - If the absolute value of `x` is less than `1`, and `y` is positive 336 * infinity, the result is `+0`. 337 * - If the absolute value of `x` is greater than `1`, and `y` is positive 338 * infinity, the result is positive infinity. 339 * - If `x` is negative infinity, and `y` is an odd integer less than `0`, 340 * the result is `-0`. 341 * - If `x` is negative infinity, and `y` less than `0` and not an odd 342 * integer, the result is `+0`. 343 * - If `x` is negative infinity, and `y` is an odd integer greater than 344 * `0`, the result is negative infinity. 345 * - If `x` is negative infinity, and `y` greater than `0` and not an odd 346 * integer, the result is positive infinity. 347 * - If `x` is positive infinity, and `y` less than `0`, the result is `+0`. 348 * - If `x` is positive infinity, and `y` greater than `0`, the result is 349 * positive infinity. 350 * 351 * Returns `NaN` if either the `x` or `y` value can't be converted to a number. 352 * 353 * @function module:math#pow 354 * 355 * @param {number} x 356 * The base value. 357 * 358 * @param {number} y 359 * The power value. 360 * 361 * @returns {number} 362 */ 363 static uc_value_t * 364 uc_pow(uc_vm_t *vm, size_t nargs) 365 { 366 double x = ucv_to_double(uc_fn_arg(0)); 367 double y = ucv_to_double(uc_fn_arg(1)); 368 369 if (isnan(x) || isnan(y)) 370 return ucv_double_new(NAN); 371 372 return ucv_double_new(pow(x, y)); 373 } 374 375 /** 376 * Produces a pseudo-random positive integer. 377 * 378 * Returns the calculated pseuo-random value. The value is within the range 379 * `0` to `RAND_MAX` inclusive where `RAND_MAX` is a platform specific value 380 * guaranteed to be at least `32767`. 381 * 382 * The {@link module:math~srand `srand()`} function sets its argument as the 383 * seed for a new sequence of pseudo-random integers to be returned by `rand()`. These sequences are 384 * repeatable by calling {@link module:math~srand `srand()`} with the same 385 * seed value. 386 * 387 * If no seed value is explicitly set by calling 388 * {@link module:math~srand `srand()`} prior to the first call to `rand()`, 389 * the math module will automatically seed the PRNG once, using the current 390 * time of day in milliseconds as seed value. 391 * 392 * @function module:math#rand 393 * 394 * @returns {number} 395 */ 396 static uc_value_t * 397 uc_rand(uc_vm_t *vm, size_t nargs) 398 { 399 struct timeval tv; 400 401 if (!srand_called) { 402 gettimeofday(&tv, NULL); 403 srand((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); 404 405 srand_called = true; 406 } 407 408 return ucv_int64_new(rand()); 409 } 410 411 /** 412 * Seeds the pseudo-random number generator. 413 * 414 * This functions seeds the PRNG with the given value and thus affects the 415 * pseudo-random integer sequence produced by subsequent calls to 416 * {@link module:math~rand `rand()`}. 417 * 418 * Setting the same seed value will result in the same pseudo-random numbers 419 * produced by {@link module:math~rand `rand()`}. 420 * 421 * @function module:math#srand 422 * 423 * @param {number} seed 424 * The seed value. 425 */ 426 static uc_value_t * 427 uc_srand(uc_vm_t *vm, size_t nargs) 428 { 429 int64_t n = ucv_to_integer(uc_fn_arg(0)); 430 431 srand((unsigned int)n); 432 srand_called = true; 433 434 return NULL; 435 } 436 437 /** 438 * Tests whether `x` is a `NaN` double. 439 * 440 * This functions checks whether the given argument is of type `double` with 441 * a `NaN` (not a number) value. 442 * 443 * Returns `true` if the value is `NaN`, otherwise false. 444 * 445 * Note that a value can also be checked for `NaN` with the expression 446 * `x !== x` which only evaluates to `true` if `x` is `NaN`. 447 * 448 * @function module:math#isnan 449 * 450 * @param {number} x 451 * The value to test. 452 * 453 * @returns {boolean} 454 */ 455 static uc_value_t * 456 uc_isnan(uc_vm_t *vm, size_t nargs) 457 { 458 uc_value_t *v = uc_fn_arg(0); 459 460 return ucv_boolean_new(ucv_type(v) == UC_DOUBLE && isnan(ucv_double_get(v))); 461 } 462 463 static const uc_function_list_t math_fns[] = { 464 { "abs", uc_abs }, 465 { "atan2", uc_atan2 }, 466 { "cos", uc_cos }, 467 { "exp", uc_exp }, 468 { "log", uc_log }, 469 { "sin", uc_sin }, 470 { "sqrt", uc_sqrt }, 471 { "pow", uc_pow }, 472 { "rand", uc_rand }, 473 { "srand", uc_srand }, 474 { "isnan", uc_isnan }, 475 }; 476 477 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 478 { 479 uc_function_list_register(scope, math_fns); 480 } 481
This page was automatically generated by LXR 0.3.1. • OpenWrt