• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/math.c

  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