• 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 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