• 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  * It should be noted that when the ucode interpreter is run as `-p "..."`,
 47  * values involving Infinity are returned as the max double precision value
 48  * +/-1e309 (JSON), whereas when run as `-e "print(...)"` Infinity is
 49  * represented by the string `Infinity`. The boolean check `isinf()` is
 50  * available to determine Infinity values.
 51  *
 52  * @module math
 53  */
 54 
 55 #include <math.h>
 56 #include <errno.h>
 57 #include <sys/time.h>
 58 
 59 #include "ucode/module.h"
 60 
 61 #ifndef M_PI
 62 #define M_PI   3.14159265358979323846264338327950288
 63 #endif
 64 #define degToRad(angleInDegrees) ((angleInDegrees) * M_PI / 180.0)
 65 #define radToDeg(angleInRadians) ((angleInRadians) * 180.0 / M_PI)
 66 
 67 
 68 /**
 69  * Returns the absolute value of the given numeric value.
 70  *
 71  * @function module:math#abs
 72  *
 73  * @param {*} number
 74  * The number to return the absolute value for.
 75  *
 76  * @returns {number}
 77  * Returns the absolute value or `NaN` if the given argument could
 78  * not be converted to a number.
 79  */
 80 static uc_value_t *
 81 uc_abs(uc_vm_t *vm, size_t nargs)
 82 {
 83         uc_value_t *v = uc_fn_arg(0), *nv, *res;
 84         int64_t n;
 85         double d;
 86 
 87         nv = v ? ucv_to_number(v) : NULL;
 88 
 89         switch (ucv_type(nv)) {
 90         case UC_INTEGER:
 91                 n = ucv_int64_get(nv);
 92 
 93                 if (n >= 0 || errno == ERANGE)
 94                         res = ucv_get(nv);
 95                 else if (n == INT64_MIN)
 96                         res = ucv_uint64_new((uint64_t)INT64_MAX + 1);
 97                 else
 98                         res = ucv_uint64_new(-n);
 99 
100                 break;
101 
102         case UC_DOUBLE:
103                 d = ucv_double_get(nv);
104 
105                 if (isnan(d) || d >= 0)
106                         res = ucv_get(nv);
107                 else
108                         res = ucv_double_new(-d);
109 
110                 break;
111 
112         default:
113                 res = ucv_double_new(NAN);
114                 break;
115         }
116 
117         ucv_put(nv);
118 
119         return res;
120 }
121 
122 /**
123  * Calculates the arc cosine of `x`.
124  *
125  * On success, this function returns the principal value of the arc
126  * cosine of `x` in radians; the return value is in the range [pi, 0].
127  *
128  *  - If `x` is -1, pi is returned.
129  *  - If `x` is  0, pi/2 is returned.
130  *  - If `x` is +1, 0 is returned.
131  *
132  * When `x` can't be converted to a numeric value, `NaN` is
133  * returned.
134  *
135  * @function module:math#acos
136  *
137  * @param {double} x
138  * The `x` value.
139  *
140  * @returns {double}
141  * @example
142  * acos(-1); // 3.1415926535898 i.e. pi
143  * acos(0);  // 1.5707963267949 i.e. pi/2
144  * acos(1);  // 0.0 i.e. 0 pi
145  */
146 static uc_value_t *
147 uc_arccos(uc_vm_t *vm, size_t nargs)
148 {
149         double x = ucv_to_double(uc_fn_arg(0));
150 
151         if (isnan(x))
152                 return ucv_double_new(NAN);
153 
154         return ucv_double_new(acos(x));
155 }
156 
157 /**
158  * Calculates the arc sine of `x`.
159  *
160  * On success, this function returns the principal value of the arc
161  * sine of `x` in radians; the return value is in the range [-pi/2, pi/2].
162  *
163  *  - If `x` is +0 (-0), 0 is returned.
164  *  - If `x` is +1 (-1), pi/2 (-pi/2) is returned.
165  *
166  * When `x` can't be converted to a numeric value, `NaN` is
167  * returned.
168  *
169  * @function module:math#asin
170  *
171  * @param {double} x
172  * The `x` value.
173  *
174  * @returns {double}
175  * @example
176  * asin(-1); // -1.5707963267949 i.e. -pi/2
177  * asin(0);  // 0.0 i.e. 0 pi
178  * asin(1);  // 1.5707963267949 i.e. pi/2
179  */
180 static uc_value_t *
181 uc_arcsin(uc_vm_t *vm, size_t nargs)
182 {
183         double x = ucv_to_double(uc_fn_arg(0));
184 
185         if (isnan(x))
186                 return ucv_double_new(NAN);
187 
188         return ucv_double_new(asin(x));
189 }
190 
191 /**
192  * Calculates the arc tangent of `x`.
193  *
194  * On success, this function returns the principal value of the arc
195  * tangent of `x` in radians; the return value is in the range [-pi/2, pi/2].
196  *
197  *  - If `x` is +0 (-0), 0 is returned.
198  *  - As `x` tends toward +Infinity (-Infinity), the return value asymptotically
199  * converges toward pi/2 (-pi/2).
200  *
201  * When `x` can't be converted to a numeric value, `NaN` is
202  * returned.
203  *
204  * @function module:math#atan
205  *
206  * @param {double} x
207  * The `x` value.
208  *
209  * @returns {double}
210  * @example
211  * atan(-100000); // -1.5707863267949 i.e. ~ -pi/2
212  * atan(0);       // 0.0 i.e. 0 pi
213  * atan(100000);  // 1.5707863267949 i.e. ~ pi/2
214  */
215 static uc_value_t *
216 uc_arctan(uc_vm_t *vm, size_t nargs)
217 {
218         double x = ucv_to_double(uc_fn_arg(0));
219 
220         if (isnan(x))
221                 return ucv_double_new(NAN);
222 
223         return ucv_double_new(atan(x));
224 }
225 
226 /**
227  * Calculates the hyperbolic cosine of `x`.
228  *
229  * On success, this function returns the principal value of the hyperbolic
230  * cosine of `x`; the return value is in the range [Infinity, 1].
231  * 
232  * The relationship is: cosh = `((e^x) + (e^-x)) / 2`.
233  *
234  *  - As `x` decreases below -1, the return value exponentiates toward Infinity.
235  *  - If `x` is  0, 1 is returned.
236  *  - As `x` increases above +1, the return value exponentiates toward Infinity.
237  *
238  * When `x` can't be converted to a numeric value, `NaN` is
239  * returned.
240  *
241  * @function module:math#cosh
242  *
243  * @param {double} x
244  * The `x` value.
245  *
246  * @returns {double}
247  * @example
248  * cosh(-10); // 11013.232920103
249  * cosh(-1);  // 1.5430806348152
250  * cosh(0);   // 1.0
251  * cosh(1);   // 1.5430806348152
252  * cosh(10);  // 11013.232920103
253  */
254 static uc_value_t *
255 uc_cosh(uc_vm_t *vm, size_t nargs)
256 {
257         double x = ucv_to_double(uc_fn_arg(0));
258 
259         if (isnan(x))
260                 return ucv_double_new(NAN);
261 
262         return ucv_double_new(cosh(x));
263 }
264 
265 /**
266  * Calculates the hyperbolic sine of `x`.
267  *
268  * On success, this function returns the principal value of the hyperbolic
269  * sine of `x`; the return value is in the range [-Infinity, Infinity].
270  *  
271  * The relationship is: sinh = `((e^x) - (e^-x)) / 2`.
272  *
273  *  - As `x` decreases below -1, the return value exponentiates toward -Infinity.
274  *  - If `x` is  0, 0 is returned.
275  *  - As `x` increases above +1, the return value exponentiates toward Infinity.
276  *
277  * When `x` can't be converted to a numeric value, `NaN` is
278  * returned.
279  *
280  * @function module:math#sinh
281  *
282  * @param {double} x
283  * The `x` value.
284  *
285  * @returns {double}
286  * @example
287  * sinh(-10); // -11013.232920103
288  * sinh(-1);  // -1.1752011936438
289  * sinh(0);   // 0.0
290  * sinh(1);   // 1.1752011936438
291  * sinh(10);  // 11013.232920103
292  */
293 static uc_value_t *
294 uc_sinh(uc_vm_t *vm, size_t nargs)
295 {
296         double x = ucv_to_double(uc_fn_arg(0));
297 
298         if (isnan(x))
299                 return ucv_double_new(NAN);
300 
301         return ucv_double_new(sinh(x));
302 }
303 
304 /**
305  * Calculates the hyperbolic tangent of `x`.
306  *
307  * On success, this function returns the principal value of the hyperbolic
308  * tangent of `x`; the return value is in the range [-1, 1].
309  *
310  * The relationship is: tanh = `((e^x) - (e^-x)) / ((e^x) + (e^-x))`, or
311  * tanh = `sinh(x) / cosh(x)`.
312  *
313  *  - As `x` decreases below -1, the return value asymptotically expands
314  * toward -1.
315  *  - If `x` is  0, 0 is returned.
316  *  - As `x` increases above +1, the return value asymptotically expands
317  * toward 1.
318  *
319  * When `x` can't be converted to a numeric value, `NaN` is
320  * returned.
321  *
322  * @function module:math#tanh
323  *
324  * @param {double} x
325  * The `x` value.
326  *
327  * @returns {double}
328  * @example
329  * atan(-100); // -1.0
330  * atan(-10);  // -0.99999999587769
331  * atan(0);    // 0.0
332  * atan(10);   // 0.99999999587769
333  * atan(100);  // 1.0
334  */
335 static uc_value_t *
336 uc_tanh(uc_vm_t *vm, size_t nargs)
337 {
338         double x = ucv_to_double(uc_fn_arg(0));
339 
340         if (isnan(x))
341                 return ucv_double_new(NAN);
342 
343         return ucv_double_new(tanh(x));
344 }
345 
346 /**
347  * Calculates the principal value of the arc tangent of `y`/`x`,
348  * using the signs of the two arguments to determine the quadrant
349  * of the result.
350  *
351  * On success, this function returns the principal value of the arc
352  * tangent of `y`/`x` in radians; the return value is in the range [-pi, pi].
353  *
354  *  - If `y` is +0 (-0) and `x` is less than 0, +pi (-pi) is returned.
355  *  - If `y` is +0 (-0) and `x` is greater than 0, +0 (-0) is returned.
356  *  - If `y` is less than 0 and `x` is +0 or -0, -pi/2 is returned.
357  *  - If `y` is greater than 0 and `x` is +0 or -0, pi/2 is returned.
358  *  - If either `x` or `y` is NaN, a NaN is returned.
359  *  - If `y` is +0 (-0) and `x` is -0, +pi (-pi) is returned.
360  *  - If `y` is +0 (-0) and `x` is +0, +0 (-0) is returned.
361  *  - If `y` is a finite value greater (less) than 0, and `x` is negative
362  *    infinity, +pi (-pi) is returned.
363  *  - If `y` is a finite value greater (less) than 0, and `x` is positive
364  *    infinity, +0 (-0) is returned.
365  *  - If `y` is positive infinity (negative infinity), and `x` is finite,
366  *    pi/2 (-pi/2) is returned.
367  *  - If `y` is positive infinity (negative infinity) and `x` is negative
368  *    infinity, +3 * pi/4 (-3 * pi/4) is returned.
369  *  - If `y` is positive infinity (negative infinity) and `x` is positive
370  *    infinity, +pi/4 (-pi/4) is returned.
371  *
372  * When either `x` or `y` can't be converted to a numeric value, `NaN` is
373  * returned.
374  *
375  * @function module:math#atan2
376  *
377  * @param {*} y
378  * The `y` value.
379  *
380  * @param {*} x
381  * The `x` value.
382  *
383  * @returns {number}
384  */
385 static uc_value_t *
386 uc_atan2(uc_vm_t *vm, size_t nargs)
387 {
388         double d1 = ucv_to_double(uc_fn_arg(0));
389         double d2 = ucv_to_double(uc_fn_arg(1));
390 
391         if (isnan(d1) || isnan(d2))
392                 return ucv_double_new(NAN);
393 
394         return ucv_double_new(atan2(d1, d2));
395 }
396 
397 /**
398  * Calculates the tangent of `x`, the floating-point value representing the
399  * angle in radians.
400  *
401  * On success, this function returns the tangent of `x`.
402  * 
403  * The relationship is `tan(x) = sin(x) / cos (x)`. A graph of the tangent has
404  * periodic patterns directly related to ratios of pi, where radian values of
405  * whole multiples of (1, 2, 3, ...) pi are 0, and radian values of half
406  * multiples of pi (1/2, 3/2, 5/2, ...) are +/-Infinity.
407  *
408  *
409  * When `x` can't be converted to a numeric value, `NaN` is
410  * returned.
411  *
412  * @function module:math#tan
413  *
414  * @param {double} x
415  * The `x` value.
416  *
417  * @returns {double}
418  */
419 static uc_value_t *
420 uc_tan(uc_vm_t *vm, size_t nargs)
421 {
422         double x = ucv_to_double(uc_fn_arg(0));
423 
424         if (isnan(x))
425                 return ucv_double_new(NAN);
426 
427         return ucv_double_new(tan(x));
428 }
429 
430 /**
431  * Calculates the cosine of `x`, where `x` is given in radians.
432  *
433  * Returns the resulting cosine value.
434  *
435  * Returns `NaN` if the `x` value can't be converted to a number.
436  *
437  * @function module:math#cos
438  *
439  * @param {number} x
440  * Radians value to calculate cosine for.
441  *
442  * @returns {number}
443  */
444 static uc_value_t *
445 uc_cos(uc_vm_t *vm, size_t nargs)
446 {
447         double d = ucv_to_double(uc_fn_arg(0));
448 
449         if (isnan(d))
450                 return ucv_double_new(NAN);
451 
452         return ucv_double_new(cos(d));
453 }
454 
455 /**
456  * Calculates the value of `e` (the base of natural logarithms)
457  * raised to the power of `x`.
458  *
459  * On success, returns the exponential value of `x`.
460  *
461  *  - If `x` is positive infinity, positive infinity is returned.
462  *  - If `x` is negative infinity, `+0` is returned.
463  *  - If the result underflows, a range error occurs, and zero is returned.
464  *  - If the result overflows, a range error occurs, and `Infinity` is returned.
465  *
466  * Returns `NaN` if the `x` value can't be converted to a number.
467  *
468  * @function module:math#exp
469  *
470  * @param {number} x
471  * Power to raise `e` to.
472  *
473  * @returns {number}
474  */
475 static uc_value_t *
476 uc_exp(uc_vm_t *vm, size_t nargs)
477 {
478         double d = ucv_to_double(uc_fn_arg(0));
479 
480         if (isnan(d))
481                 return ucv_double_new(NAN);
482 
483         return ucv_double_new(exp(d));
484 }
485 
486 /**
487  * Calculates the natural logarithm of `x`.
488  *
489  * On success, returns the natural logarithm of `x`.
490  *
491  *  - If `x` is `1`, the result is `+0`.
492  *  - If `x` is positive infinity, positive infinity is returned.
493  *  - If `x` is zero, then a pole error occurs, and the function
494  *    returns negative infinity.
495  *  - If `x` is negative (including negative infinity), then a domain
496  *    error occurs, and `NaN` is returned.
497  *
498  * Returns `NaN` if the `x` value can't be converted to a number.
499  *
500  * @function module:math#log
501  *
502  * @param {number} x
503  * Value to calculate natural logarithm of.
504  *
505  * @returns {number}
506  */
507 static uc_value_t *
508 uc_log(uc_vm_t *vm, size_t nargs)
509 {
510         double d = ucv_to_double(uc_fn_arg(0));
511 
512         if (isnan(d))
513                 return ucv_double_new(NAN);
514 
515         return ucv_double_new(log(d));
516 }
517 
518 /**
519  * Calculate base-10 log of x.
520  *
521  * @function module:math#log10
522  *
523  * @param {double} x number
524  *
525  * @returns {double}
526  * The common (base-10) logarithm of x, or
527  * `NaN` if the given argument could not be converted to a number.
528  *
529  * @example
530  * log10(100);   // 2.0
531  * log10(10);    // 1.0
532  * log10(5);     // 0.69897000433602
533  * log10(1);     // 0.0
534  * log10(0);     // -1e309
535  */
536 static uc_value_t *
537 uc_log10(uc_vm_t *vm, size_t nargs)
538 {
539         double x = ucv_to_double(uc_fn_arg(0));
540 
541         if (isnan(x))
542                 return ucv_double_new(NAN);
543 
544         return ucv_double_new(log10(x));
545 }
546 
547 /**
548  * Calculate base-2 log of x.
549  *
550  * @function module:math#log2
551  *
552  * @param {double} x number
553  *
554  * @returns {double}
555  * The common (base-2) logarithm of x, or
556  * `NaN` if the given argument could not be converted to a number.
557  *
558  * @example
559  * log2(1024);  // 10.0
560  * log2(512);   // 9.0
561  * log2(16);    // 4.0
562  * log2(4);     // 2.0
563  * log2(2);     // 1.0
564  * log2(1);     // 0.0
565  * log2(0);     // -1e309
566  */
567 static uc_value_t *
568 uc_log2(uc_vm_t *vm, size_t nargs)
569 {
570         double x = ucv_to_double(uc_fn_arg(0));
571 
572         if (isnan(x))
573                 return ucv_double_new(NAN);
574 
575         return ucv_double_new(log2(x));
576 }
577 
578 /**
579  * Computes the natural (base e) logarithm of 1 + x. This function is more
580  * precise than the expression {@link module:math#log `log`}(1 + x) if x is
581  * close to zero.
582  *
583  * @function module:math#log1p
584  *
585  * @param {double} x number
586  *
587  * @returns {double}
588  * The natural (base e) logarithm of 1 + x, or
589  * `NaN` if the given argument could not be converted to a number.
590  *
591  * @example
592  * log1p(10);    // 2.3978952727984
593  * log1p(1);     // 0.69314718055995
594  * log1p(0.1);   // 0.095310179804325
595  * log1p(0.001); // 0.00099950033308353
596  * log1p(0);     // 0.0
597  */
598 static uc_value_t *
599 uc_log1p(uc_vm_t *vm, size_t nargs)
600 {
601         double x = ucv_to_double(uc_fn_arg(0));
602 
603         if (isnan(x))
604                 return ucv_double_new(NAN);
605 
606         return ucv_double_new(log1p(x));
607 }
608 
609 /**
610  * Computes the e (Euler's number, 2.7182818) raised to the given power x,
611  * minus 1.0. This function is more accurate than the expression 
612  * {@link module:math#exp `exp(x)`}-1.0
613  * if x is close to zero.
614  *
615  * @function module:math#expm1
616  *
617  * @param {double} x number
618  *
619  * @returns {double}
620  * The e (Euler's number, 2.7182818) raised to the given power x, minus 1.0, or
621  * `NaN` if the given argument could not be converted to a number.
622  *
623  * @example
624  * expm1(10);       // 22025.465794807
625  * expm1(1);        // 1.718281828459
626  * expm1(0.1);      // 0.10517091807565
627  * expm1(0.001);    // 0.0010005001667083
628  * exp(0.001)-1;    // 0.0010005001667084
629  * expm1(0.0001);   // 0.00010000500016667
630  * exp(0.0001)-1;   // 0.00010000500016671
631  * expm1(0.000001); // 1.0000005000002e-06
632  * exp(0.000001)-1; // 1.0000004999622e-06
633  * expm1(0);        // 0.0
634  */
635 static uc_value_t *
636 uc_expm1(uc_vm_t *vm, size_t nargs)
637 {
638         double x = ucv_to_double(uc_fn_arg(0));
639 
640         if (isnan(x))
641                 return ucv_double_new(NAN);
642 
643         return ucv_double_new(expm1(x));
644 }
645 
646 /**
647  * Calculates the sine of `x`, where `x` is given in radians.
648  *
649  * Returns the resulting sine value.
650  *
651  *  - When `x` is positive or negative infinity, a domain error occurs
652  *    and `NaN` is returned.
653  *
654  * Returns `NaN` if the `x` value can't be converted to a number.
655  *
656  * @function module:math#sin
657  *
658  * @param {number} x
659  * Radians value to calculate sine for.
660  *
661  * @returns {number}
662  */
663 static uc_value_t *
664 uc_sin(uc_vm_t *vm, size_t nargs)
665 {
666         double d = ucv_to_double(uc_fn_arg(0));
667 
668         if (isnan(d))
669                 return ucv_double_new(NAN);
670 
671         return ucv_double_new(sin(d));
672 }
673 
674 /**
675  * Calculates the non-negative square root of `x`.
676  *
677  * Returns the resulting square root value.
678  *
679  *  - If `x` is `+0` (`-0`) then `+0` (`-0`) is returned.
680  *  - If `x` is positive infinity, positive infinity is returned.
681  *  - If `x` is less than `-0`, a domain error occurs, and `NaN` is returned.
682  *
683  * Returns `NaN` if the `x` value can't be converted to a number.
684  *
685  * @function module:math#sqrt
686  *
687  * @param {number} x
688  * Value to calculate square root for.
689  *
690  * @returns {number}
691  */
692 static uc_value_t *
693 uc_sqrt(uc_vm_t *vm, size_t nargs)
694 {
695         double d = ucv_to_double(uc_fn_arg(0));
696 
697         if (isnan(d))
698                 return ucv_double_new(NAN);
699 
700         return ucv_double_new(sqrt(d));
701 }
702 
703 /**
704  * Calculates the cube root of `x`.
705  *
706  * Returns the resulting cube root value.
707  *
708  *  - If `x` is `+0` (`-0`) then `+0` (`-0`) is returned.
709  *  - If `x` is (+/-) infinity, (+/-) infinity is returned.
710  *
711  * Returns `NaN` if the `x` value can't be converted to a number.
712  *
713  * @function module:math#cbrt
714  *
715  * @param {double} x
716  * Value to calculate cube root for.
717  *
718  * @returns {double}
719  * @example
720  * cbrt(27);  // 3.0
721  * cbrt(0);   // 0.0
722  * cbrt(-27); // -3.0
723  */
724 static uc_value_t *
725 uc_cbrt(uc_vm_t *vm, size_t nargs)
726 {
727         double x = ucv_to_double(uc_fn_arg(0));
728 
729         if (isnan(x))
730                 return ucv_double_new(NAN);
731 
732         return ucv_double_new(cbrt(x));
733 }
734 
735 /**
736  * Computes the square root of the sum of the squares of `x` and `y`, i.e.
737  * the hypotenuse without undue overflow or underflow at intermediate stages of
738  * the computation.
739  *
740  * Returns the result of `sqrt(x^2 + y^2)`.
741  *
742  *  - If `x` and `y` are `+0` (`-0`) then `+0` (`-0`) is returned.
743  *  - If `x` or `y` is `+0` (`-0`) then `+y` or `+x` is returned.
744  *  - If `x` or `y` is (+/-) infinity, (+/-) infinity is returned.
745  *
746  * Returns `NaN` if the `x` or `y` value can't be converted to a number.
747  *
748  * @function module:math#hypot
749  *
750  * @param {double} x base
751  * @param {double} y height
752  *
753  * @returns {double}
754  * @example
755  * hypot(3, 3);   // 4.2426406871193
756  * hypot(2, 2);   // 2.8284271247462
757  * hypot(1, 1);   // 1.4142135623731
758  * hypot(0, 0);   // 0.0
759  * hypot(-1, -1); // -1.4142135623731
760  */
761 static uc_value_t *
762 uc_hypot(uc_vm_t *vm, size_t nargs)
763 {
764         double x = ucv_to_double(uc_fn_arg(0));
765         double y = ucv_to_double(uc_fn_arg(1));
766 
767         if (isnan(x) || isnan(y))
768                 return ucv_double_new(NAN);
769 
770         return ucv_double_new(hypot(x, y));
771 }
772 
773 /**
774  * Calculates the value of `x` raised to the power of `y`.
775  *
776  * On success, returns the value of `x` raised to the power of `y`.
777  *
778  *  - If the result overflows, a range error occurs, and the function
779  *    returns `Infinity`.
780  *  - If result underflows, and is not representable, a range error
781  *    occurs, and `0.0` with the appropriate sign is returned.
782  *  - If `x` is `+0` or `-0`, and `y` is an odd integer less than `0`,
783  *    a pole error occurs `Infinity` is returned, with the same sign
784  *    as `x`.
785  *  - If `x` is `+0` or `-0`, and `y` is less than `0` and not an odd
786  *    integer, a pole error occurs and `Infinity` is returned.
787  *  - If `x` is `+0` (`-0`), and `y` is an odd integer greater than `0`,
788  *    the result is `+0` (`-0`).
789  *  - If `x` is `0`, and `y` greater than `0` and not an odd integer,
790  *    the result is `+0`.
791  *  - If `x` is `-1`, and `y` is positive infinity or negative infinity,
792  *    the result is `1.0`.
793  *  - If `x` is `+1`, the result is `1.0` (even if `y` is `NaN`).
794  *  - If `y` is `0`, the result is `1.0` (even if `x` is `NaN`).
795  *  - If `x` is a finite value less than `0`, and `y` is a finite
796  *    non-integer, a domain error occurs, and `NaN` is returned.
797  *  - If the absolute value of `x` is less than `1`, and `y` is negative
798  *    infinity, the result is positive infinity.
799  *  - If the absolute value of `x` is greater than `1`, and `y` is
800  *    negative infinity, the result is `+0`.
801  *  - If the absolute value of `x` is less than `1`, and `y` is positive
802  *    infinity, the result is `+0`.
803  *  - If the absolute value of `x` is greater than `1`, and `y` is positive
804  *    infinity, the result is positive infinity.
805  *  - If `x` is negative infinity, and `y` is an odd integer less than `0`,
806  *    the result is `-0`.
807  *  - If `x` is negative infinity, and `y` less than `0` and not an odd
808  *    integer, the result is `+0`.
809  *  - If `x` is negative infinity, and `y` is an odd integer greater than
810  *    `0`, the result is negative infinity.
811  *  - If `x` is negative infinity, and `y` greater than `0` and not an odd
812  *    integer, the result is positive infinity.
813  *  - If `x` is positive infinity, and `y` less than `0`, the result is `+0`.
814  *  - If `x` is positive infinity, and `y` greater than `0`, the result is
815  *    positive infinity.
816  *
817  * Returns `NaN` if either the `x` or `y` value can't be converted to a number.
818  *
819  * @function module:math#pow
820  *
821  * @param {number} x
822  * The base value.
823  *
824  * @param {number} y
825  * The power value.
826  *
827  * @returns {number}
828  */
829 static uc_value_t *
830 uc_pow(uc_vm_t *vm, size_t nargs)
831 {
832         double x = ucv_to_double(uc_fn_arg(0));
833         double y = ucv_to_double(uc_fn_arg(1));
834 
835         if (isnan(x) || isnan(y))
836                 return ucv_double_new(NAN);
837 
838         return ucv_double_new(pow(x, y));
839 }
840 
841 /**
842  * Depending on the arguments, it produces a pseudo-random positive integer, 
843  * or a pseudo-random number in a supplied range.
844  *
845  * Without arguments it returns the calculated pseuo-random value. The value 
846  * is within the range `0` to `RAND_MAX` inclusive where `RAND_MAX` is a platform 
847  * specific value guaranteed to be at least `32767`.
848  * 
849  * With 2 arguments `a, b` it returns a number in the range `a` to `b` inclusive.
850  * With a single argument `a` it returns a number in the range `0` to `a` inclusive.
851  * 
852  * The {@link module:math#srand `srand()`} function sets its argument as the
853  * seed for a new sequence of pseudo-random integers to be returned by `rand()`.
854  * These sequences are repeatable by calling {@link module:math#srand `srand()`}
855  * with the same seed value.
856  *
857  * If no seed value is explicitly set by calling
858  * {@link module:math#srand `srand()`} prior to the first call to `rand()`,
859  * the math module will automatically seed the PRNG once, using the current
860  * time of day in milliseconds as seed value.
861  *
862  * @function module:math#rand
863  *
864  * @param {number} [a]
865  * End of the desired range.
866  * 
867  * @param {number} [b]
868  * The other end of the desired range.
869  * 
870  * @returns {number}
871  */
872 static uc_value_t *
873 uc_rand(uc_vm_t *vm, size_t nargs)
874 {
875         struct timeval tv;
876 
877         if (!ucv_boolean_get(uc_vm_registry_get(vm, "math.srand_called"))) {
878                 gettimeofday(&tv, NULL);
879                 srand((tv.tv_sec * 1000) + (tv.tv_usec / 1000));
880 
881                 uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(true));
882         }
883 
884         if (nargs == 0)
885                 return ucv_int64_new(rand());
886 
887         double a = ucv_to_double(uc_fn_arg(0)), b = 0;
888 
889         if (nargs > 1)
890                 b = ucv_to_double(uc_fn_arg(1));
891 
892         return ucv_double_new(a + ((b - a) * rand()) / RAND_MAX);
893 }
894 
895 /**
896  * Seeds the pseudo-random number generator.
897  *
898  * This functions seeds the PRNG with the given value and thus affects the
899  * pseudo-random integer sequence produced by subsequent calls to
900  * {@link module:math#rand `rand()`}.
901  *
902  * Setting the same seed value will result in the same pseudo-random numbers
903  * produced by {@link module:math#rand `rand()`}.
904  *
905  * @function module:math#srand
906  *
907  * @param {number} seed
908  * The seed value.
909  */
910 static uc_value_t *
911 uc_srand(uc_vm_t *vm, size_t nargs)
912 {
913         int64_t n = ucv_to_integer(uc_fn_arg(0));
914 
915         srand((unsigned int)n);
916         uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(true));
917 
918         return NULL;
919 }
920 
921 /**
922  * Tests whether `x` is a `NaN` double.
923  *
924  * This functions checks whether the given argument is of type `double` with
925  * a `NaN` (not a number) value.
926  *
927  * Returns `true` if the value is `NaN`, otherwise false.
928  *
929  * Note that a value can also be checked for `NaN` with the expression
930  * `x !== x` which only evaluates to `true` if `x` is `NaN`.
931  *
932  * @function module:math#isnan
933  *
934  * @param {number} x
935  * The value to test.
936  *
937  * @returns {boolean}
938  */
939 static uc_value_t *
940 uc_isnan(uc_vm_t *vm, size_t nargs)
941 {
942         uc_value_t *v = uc_fn_arg(0);
943 
944         return ucv_boolean_new(ucv_type(v) == UC_DOUBLE && isnan(ucv_double_get(v)));
945 }
946 
947 /**
948  * Tests whether `x` is double precision `Infinity`.
949  *
950  * This functions checks whether the given argument is of type `double` of
951  * `Infinity` value. Double precision values >= 1.8e308 are considered `Infinity`.
952  *
953  * Returns `true` if the value is `Infinity`, otherwise false.
954  *
955  * @function module:math#isinf
956  *
957  * @param {number} x
958  * The value to test.
959  *
960  * @returns {boolean}
961  */
962 static uc_value_t *
963 uc_isinf(uc_vm_t *vm, size_t nargs)
964 {
965         uc_value_t *v = uc_fn_arg(0);
966 
967         return ucv_boolean_new(ucv_type(v) == UC_DOUBLE && isinf(ucv_double_get(v)));
968 }
969 
970 /**
971  * Returns the radian value of the given degree value.
972  *
973  * @function module:math#deg2rad
974  *
975  * @param {double} number
976  * The number to return the radian value for.
977  *
978  * @returns {number}
979  * Returns the absolute value or `NaN` if the given argument could
980  * not be converted to a number.
981  * @example
982  * deg2rad(180);   // 3.1415926535898
983  * deg2rad("180"); // 3.1415926535898
984  */
985 static uc_value_t *
986 uc_deg2rad(uc_vm_t *vm, size_t nargs)
987 {
988         double d = ucv_to_double(uc_fn_arg(0));
989 
990         return ucv_double_new(degToRad(d));
991 }
992 
993 /**
994  * Returns the degree value of the given radian value.
995  *
996  * @function module:math#rad2deg
997  *
998  * @param {double} number
999  * The number to return the degree value for.
1000  *
1001  * @returns {number}
1002  * Returns the absolute value or `NaN` if the given argument could
1003  * not be converted to a number.
1004  * @example
1005  * rad2deg(3.1415926535898);   // 180.0
1006  * rad2deg("3.1415926535898"); // 180.0
1007  */
1008 static uc_value_t *
1009 uc_rad2deg(uc_vm_t *vm, size_t nargs)
1010 {
1011         double d = ucv_to_double(uc_fn_arg(0));
1012 
1013         return ucv_double_new(radToDeg(d));
1014 }
1015 
1016 /**
1017  * Returns the lesser of two values x and y.
1018  *
1019  * @function module:math#fmin
1020  *
1021  * @param {double} x first parameter
1022  * @param {double} y second parameter
1023  *
1024  * @returns {double}
1025  * Returns the lesser of the two values x or y or `NaN` if a given argument
1026  * could not be converted to a number. Use `(-)Infinity` or `NAN` for
1027  * comparisons involving said values.
1028  * @example
1029  * fmin("180", "-180");   // -180.0
1030  * fmin(180, -180);       // -180.0
1031  * fmin(-Infinity, 0);    // -1e309 i.e. -infinity in double type representation.
1032  */
1033 static uc_value_t *
1034 uc_fmin(uc_vm_t *vm, size_t nargs)
1035 {
1036         double x = ucv_to_double(uc_fn_arg(0));
1037         double y = ucv_to_double(uc_fn_arg(1));
1038 
1039         if (isnan(x) || isnan(y))
1040                 return ucv_double_new(NAN);
1041 
1042         return ucv_double_new(fmin(x, y));
1043 }
1044 
1045 /**
1046  * Returns the greater of two values x and y.
1047  *
1048  * @function module:math#fmax
1049  *
1050  * @param {double} x first parameter
1051  * @param {double} y second parameter
1052  *
1053  * @returns {double}
1054  * Returns the greater of the two values x or y or `NaN` if a given argument
1055  * could not be converted to a number. Use `(-)Infinity` or `NAN` for
1056  * comparisons involving said values.
1057  * @example
1058  * fmax("180", "-180");   // 180.0
1059  * fmax(180, -180);       // 180.0
1060  * fmax(Infinity, 0);     // 1e309 i.e. infinity in double type representation.
1061  */
1062 static uc_value_t *
1063 uc_fmax(uc_vm_t *vm, size_t nargs)
1064 {
1065         double x = ucv_to_double(uc_fn_arg(0));
1066         double y = ucv_to_double(uc_fn_arg(1));
1067 
1068         if (isnan(x) || isnan(y))
1069                 return ucv_double_new(NAN);
1070 
1071         return ucv_double_new(fmax(x, y));
1072 }
1073 
1074 /**
1075  * Clamps `x` to within `upper` and `lower` bounds if `x` exceeds them.
1076  *
1077  * The operation is effectively: `min(upper, max(x, lower))`.
1078  *
1079  * @function module:math#clamp
1080  *
1081  * @param {double} x number to clamp
1082  * @param {double} upper upper bound
1083  * @param {double} lower lower bound
1084  *
1085  * @returns {double}
1086  * Returns `x` if within `upper` and `lower`, otherwise one of `lower` or `upper`
1087  * if `x` exceeds those bounds, or `NaN` if any given argument
1088  * could not be converted to a number.
1089  * @example
1090  * clamp(1000, 200, 180);   // 200.0
1091  * clamp(-1000, 200, 180);  // 180.0
1092  * clamp(190, 200, 180);    // 190.0
1093  */
1094 static uc_value_t *
1095 uc_clamp(uc_vm_t *vm, size_t nargs)
1096 {
1097         double x = ucv_to_double(uc_fn_arg(0));
1098         double upper = ucv_to_double(uc_fn_arg(1));
1099         double lower = ucv_to_double(uc_fn_arg(2));
1100 
1101         if (isnan(x) || isnan(upper) || isnan(lower))
1102                 return ucv_double_new(NAN);
1103 
1104         return ucv_double_new(fmin(upper, fmax(x, lower)));
1105 }
1106 
1107 /**
1108  * Returns -1 or 1 depending on the sign of the given number, or 0 if the given
1109  * number itself is zero.
1110  *
1111  * @function module:math#sign
1112  *
1113  * @param {double} x number
1114  *
1115  * @returns {integer}
1116  * Returns -1 or 1 for negative and positive inputs respectively, 0 if the given
1117  * number is zero, or `NaN` if the given argument could not be converted to a
1118  * number.
1119  * @example
1120  * sign(2);   // 1
1121  * sign(-8);  // -1
1122  * sign(0);   // 0
1123  * sign(-0);  // 0
1124  */
1125 static uc_value_t *
1126 uc_sign(uc_vm_t *vm, size_t nargs)
1127 {
1128         double x = ucv_to_double(uc_fn_arg(0));
1129 
1130         if (isnan(x))
1131                 return ucv_double_new(NAN);
1132 
1133         return ucv_int64_new((x > 0) - (x < 0));
1134 }
1135 
1136 /**
1137  * Returns -1 or 1 depending on the sign of the given number, or 0 if the given
1138  * number itself is zero. IEEE-754 behaviour.
1139  *
1140  * @function module:math#signbit
1141  *
1142  * @param {double} x number
1143  *
1144  * @returns {integer}
1145  * Returns -1 or 1 for negative and positive inputs respectively, 0 if the given
1146  * number is zero, -1 for -0.0, or `NaN` if the given argument could not be
1147  * converted to a number.
1148  * @example
1149  * signbit(2);    // 1
1150  * signbit(-8);   // -1
1151  * signbit(0);    // 0
1152  * signbit(-0.0); // -1
1153  * signbit(-0);   // 0
1154  */
1155 static uc_value_t *
1156 uc_signbit(uc_vm_t *vm, size_t nargs)
1157 {
1158         double x = ucv_to_double(uc_fn_arg(0));
1159 
1160         if (isnan(x))
1161                 return ucv_double_new(NAN);
1162 
1163         return ucv_int64_new(signbit(x) ? -1 : (x > 0));
1164 }
1165 
1166 /**
1167  * Returns -1 or 1 depending on the sign of the given number only (no zero).
1168  * (-)Zero effectively becomes 1.
1169  *
1170  * @function module:math#signnz
1171  *
1172  * @param {double} x number
1173  *
1174  * @returns {integer}
1175  * Returns -1 or +1 for negative and positive inputs respectively, and zero is
1176  * converted to +1, or `NaN` if the given argument could not be converted to a
1177  * number.
1178  * @example
1179  * signnz(2);   // 1
1180  * signnz(-8);  // -1
1181  * signnz(0);   // 1
1182  * signnz(-0);  // 1
1183  */
1184 static uc_value_t *
1185 uc_signnz(uc_vm_t *vm, size_t nargs)
1186 {
1187         double x = ucv_to_double(uc_fn_arg(0));
1188 
1189         if (isnan(x))
1190                 return ucv_double_new(NAN);
1191 
1192         return ucv_int64_new((x >= 0) ? 1 : -1);
1193 }
1194 
1195 /**
1196  * Returns a double whose magnitude is that of `x`, but whose sign is that of
1197  * `y`.
1198  *
1199  * @function module:math#copysign
1200  *
1201  * @param {double} x number
1202  * @param {double} y number
1203  *
1204  * @returns {double}
1205  * Returns `NaN` if a given argument could not be converted to a number.
1206  * @example
1207  * copysign(-3, -5);  // -3.0
1208  * copysign(8, -5);   // -8.0
1209  * copysign(-0, 3);   // 0.0
1210  */
1211 static uc_value_t *
1212 uc_copysign(uc_vm_t *vm, size_t nargs)
1213 {
1214         double x = ucv_to_double(uc_fn_arg(0));
1215         double y = ucv_to_double(uc_fn_arg(1));
1216 
1217         if (isnan(x) || isnan(y))
1218                 return ucv_double_new(NAN);
1219 
1220         return ucv_double_new(copysign(x, y));
1221 }
1222 
1223 /**
1224  * Floors `x` to the largest integer value not greater than `x`.
1225  *
1226  * @function module:math#floor
1227  *
1228  * @param {double} x number
1229  * @param {boolean} output_type - false is `integer`, true is `double`.
1230  *
1231  * @returns {number}
1232  * Returns the largest integer value not greater than `x`, or `NaN` if the given
1233  * argument could not be converted to a number.
1234  * @example
1235  * floor(2.7);        // 2
1236  * floor(2.7, true);  // 2.0
1237  * floor(-2.7);       // -3
1238  * floor(-0.0);       // 0
1239  * floor(-0.0, true); // -0.0
1240  * floor(-Infinity);  // -1e309 i.e. -infinity in double type representation.
1241  */
1242 static uc_value_t *
1243 uc_floor(uc_vm_t *vm, size_t nargs)
1244 {
1245         double x = ucv_to_double(uc_fn_arg(0));
1246         bool ot = ucv_boolean_get(uc_fn_arg(1));
1247 
1248         if (isnan(x))
1249                 return ucv_double_new(NAN);
1250 
1251         return ot ? ucv_double_new(floor(x)) : ucv_int64_new(floor(x));
1252 }
1253 
1254 /**
1255  * Computes the smallest integer value not less than `x`.
1256  *
1257  * @function module:math#ceil
1258  *
1259  * @param {double} x number
1260  * @param {boolean} output_type - false is `integer`, true is `double`.
1261  *
1262  * @returns {number}
1263  * Returns the smallest integer value not less than `x`, or `NaN` if the given
1264  * argument could not be converted to a number.
1265  * @example
1266  * ceil(2.7);        // 3
1267  * ceil(2.7, true);  // 3.0
1268  * ceil(-2.7);       // -2
1269  * ceil(-0.0);       // 0
1270  * ceil(-0.0, true); // -0.0
1271  * ceil(-Infinity);  // -1e309 i.e. -infinity in double type representation.
1272  */
1273 static uc_value_t *
1274 uc_ceil(uc_vm_t *vm, size_t nargs)
1275 {
1276         double x = ucv_to_double(uc_fn_arg(0));
1277         bool ot = ucv_boolean_get(uc_fn_arg(1));
1278 
1279         if (isnan(x))
1280                 return ucv_double_new(NAN);
1281 
1282         return ot ? ucv_double_new(ceil(x)) : ucv_int64_new(ceil(x));
1283 }
1284 
1285 /**
1286  * Returns the integral value nearest to x rounding half-way cases away
1287  * from zero, regardless of the current rounding direction.
1288  *
1289  * @function module:math#round
1290  *
1291  * @param {double} x number
1292  * @param {boolean} output_type - false is `integer`, true is `double`.
1293  *
1294  * @returns {number}
1295  * Returns the rounded integer value of `x`, or `NaN` if the given
1296  * argument could not be converted to a number.
1297  *
1298  * @example
1299  * round(2.4);        // 2
1300  * round(2.5);        // 3
1301  * round(2.7);        // 3
1302  * round(2.7, true);  // 3.0
1303  * round(-2.4);       // -2
1304  * round(-2.5);       // -3
1305  * round(-2.7);       // -3
1306  * round(-0.0);       // 0
1307  * round(-0.0, true); // -0.0
1308  * round(-Infinity);  // -1e309 i.e. -infinity in double type representation.
1309  */
1310 static uc_value_t *
1311 uc_round(uc_vm_t *vm, size_t nargs)
1312 {
1313         double x = ucv_to_double(uc_fn_arg(0));
1314         bool ot = ucv_boolean_get(uc_fn_arg(1));
1315 
1316         if (isnan(x))
1317                 return ucv_double_new(NAN);
1318 
1319         return ot ? ucv_double_new(round(x)) : ucv_int64_new(round(x));
1320 }
1321 
1322 /**
1323  * Truncate away the decimal portion to produce the nearest integer not greater
1324  * in magnitude than x.
1325  *
1326  * @function module:math#trunc
1327  *
1328  * @param {number} x number
1329  * @param {boolean} output_type - false is `integer`, true is `double`.
1330  *
1331  * @returns {number}
1332  * The integral portion remaining after the decimal portion is truncated, or
1333  * `NaN` if the given argument could not be converted to a number.
1334  *
1335  * @example
1336  * trunc(2.4);        // 2
1337  * trunc(2.5);        // 2
1338  * trunc(2.7);        // 2
1339  * trunc(2.7, true);  // 2.0
1340  * trunc(-2.4);       // -2
1341  * trunc(-2.5);       // -2
1342  * trunc(-2.7);       // -2
1343  * trunc(-0.0);       // 0
1344  * trunc(-0.0, true); // -0.0
1345  */
1346 static uc_value_t *
1347 uc_trunc(uc_vm_t *vm, size_t nargs)
1348 {
1349         double x = ucv_to_double(uc_fn_arg(0));
1350         bool ot = ucv_boolean_get(uc_fn_arg(1));
1351 
1352         if (isnan(x))
1353                 return ucv_double_new(NAN);
1354 
1355         return ot ? ucv_double_new(trunc(x)) : ucv_int64_new(trunc(x));
1356 }
1357 
1358 static const uc_function_list_t math_fns[] = {
1359         { "abs",                uc_abs },
1360         { "acos",               uc_arccos },
1361         { "asin",               uc_arcsin },
1362         { "atan",               uc_arctan },
1363         { "atan2",              uc_atan2 },
1364         { "cosh",               uc_cosh },
1365         { "sinh",               uc_sinh },
1366         { "tanh",               uc_tanh },
1367         { "tan",                uc_tan },
1368         { "cos",                uc_cos },
1369         { "exp",                uc_exp },
1370         { "expm1",              uc_expm1 },
1371         { "log",                uc_log },
1372         { "log1p",              uc_log1p },
1373         { "log10",              uc_log10 },
1374         { "log2",               uc_log2 },
1375         { "sin",                uc_sin },
1376         { "sqrt",               uc_sqrt },
1377         { "hypot",              uc_hypot },
1378         { "cbrt",               uc_cbrt },
1379         { "pow",                uc_pow },
1380         { "rand",               uc_rand },
1381         { "srand",              uc_srand },
1382         { "isnan",              uc_isnan },
1383         { "isinf",              uc_isinf },
1384         { "deg2rad",    uc_deg2rad },
1385         { "rad2deg",    uc_rad2deg },
1386         { "fmin",               uc_fmin },
1387         { "fmax",               uc_fmax },
1388         { "clamp",              uc_clamp },
1389         { "sign",               uc_sign },
1390         { "signbit",    uc_signbit },
1391         { "signnz",             uc_signnz },
1392         { "copysign",   uc_copysign },
1393         { "floor",              uc_floor },
1394         { "ceil",               uc_ceil },
1395         { "round",              uc_round },
1396         { "trunc",              uc_trunc },
1397 };
1398 
1399 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1400 {
1401         uc_function_list_register(scope, math_fns);
1402 
1403         uc_vm_registry_set(vm, "math.srand_called", ucv_boolean_new(false));
1404 }
1405 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt