]> Lady’s Gitweb - Pisces/blob - numeric.js
Move isConcatSpreadable[Object] into object.js
[Pisces] / numeric.js
1 // ♓🌟 Piscēs ∷ numeric.js
2 // ====================================================================
3 //
4 // Copyright © 2022–2023 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import { call, createArrowFunction } from "./function.js";
11 import {
12 stringCatenate,
13 stringPadEnd,
14 stringRepeat,
15 substring,
16 toString,
17 } from "./string.js";
18 import { sameValue, toPrimitive } from "./value.js";
19
20 export const {
21 /**
22 * ln(10).
23 *
24 * ※ This is an alias for `Math.LN10`.
25 */
26 LN10,
27
28 /**
29 * ln(2).
30 *
31 * ※ This is an alias for `Math.LN2`.
32 */
33 LN2,
34
35 /**
36 * log10(ℇ).
37 *
38 * ※ This is an alias for `Math.LOG10E`.
39 */
40 LOG10E: LOG10ℇ,
41
42 /**
43 * log2(ℇ).
44 *
45 * ※ This is an alias for `Math.LOG2E`.
46 */
47 LOG2E: LOG2ℇ,
48
49 /**
50 * sqrt(½).
51 *
52 * ※ This is an alias for `Math.SQRT1_2`.
53 */
54 SQRT1_2: RECIPROCAL_SQRT2,
55
56 /**
57 * sqrt(2).
58 *
59 * ※ This is an alias for `Math.SQRT2`.
60 */
61 SQRT2,
62
63 /**
64 * The mathematical constant π.
65 *
66 * ※ This is an alias for `Math.PI`.
67 */
68 PI: Π,
69
70 /**
71 * The Euler number.
72 *
73 * ※ This is an alias for `Math.E`.
74 */
75 E: ℇ,
76 } = Math;
77
78 export const {
79 /**
80 * The largest number value less than infinity.
81 *
82 * ※ This is an alias for `Number.MAX_VALUE`.
83 */
84 MAX_VALUE: MAXIMUM_NUMBER,
85
86 /**
87 * 2**53 - 1.
88 *
89 * ※ This is an alias for `Number.MAX_SAFE_INTEGER`.
90 */
91 MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER,
92
93 /**
94 * The smallest number value greater than negative infinity.
95 *
96 * ※ This is an alias for `Number.MIN_VALUE`.
97 */
98 MIN_VALUE: MINIMUM_NUMBER,
99
100 /**
101 * -(2**53 - 1).
102 *
103 * ※ This is an alias for `Number.MIN_SAFE_INTEGER`.
104 */
105 MIN_SAFE_INTEGER: MINIMUM_SAFE_INTEGRAL_NUMBER,
106
107 /**
108 * Negative infinity.
109 *
110 * ※ This is an alias for `Number.NEGATIVE_INFINITY`.
111 */
112 NEGATIVE_INFINITY,
113
114 /**
115 * Nan.
116 *
117 * ※ This is an alias for `Number.NaN`.
118 */
119 NaN: NAN,
120
121 /**
122 * Positive infinity.
123 *
124 * ※ This is an alias for `Number.POSITIVE_INFINITY`.
125 */
126 POSITIVE_INFINITY,
127
128 /**
129 * The difference between 1 and the smallest number greater than 1.
130 *
131 * ※ This is an alias for `Number.EPSILON`.
132 */
133 EPSILON: Ε,
134 } = Number;
135
136 /** Negative zero. */
137 export const NEGATIVE_ZERO = -0;
138
139 /** Positive zero. */
140 export const POSITIVE_ZERO = 0;
141
142 /**
143 * Returns the magnitude (absolute value) of the provided value.
144 *
145 * ※ Unlike `Math.abs`, this function can take big·int arguments.
146 */
147 export const abs = ($) => {
148 const n = toNumeric($);
149 return typeof n === "bigint"
150 ? n < 0n ? -n : n
151 : isNan(n)
152 ? NAN
153 : sameValue(n, -0)
154 ? 0
155 : sameValue(n, NEGATIVE_INFINITY)
156 ? POSITIVE_INFINITY
157 : n < 0
158 ? -n
159 : n;
160 };
161
162 /**
163 * Returns the arccos of the provided value.
164 *
165 * ※ This function is effectively an alias for `Math.acos`.
166 *
167 * ☡ This function does not allow big·int arguments.
168 */
169 export const arccos = createArrowFunction(
170 Math.acos,
171 { name: "arccos" },
172 );
173
174 /**
175 * Returns the arccosh of the provided value.
176 *
177 * ※ This function is effectively an alias for `Math.acosh`.
178 *
179 * ☡ This function does not allow big·int arguments.
180 */
181 export const arccosh = createArrowFunction(
182 Math.acosh,
183 { name: "arccosh" },
184 );
185
186 /**
187 * Returns the arcsin of the provided value.
188 *
189 * ※ This function is effectively an alias for `Math.asin`.
190 *
191 * ☡ This function does not allow big·int arguments.
192 */
193 export const arcsin = createArrowFunction(
194 Math.asin,
195 { name: "arcsin" },
196 );
197
198 /**
199 * Returns the arcsinh of the provided value.
200 *
201 * ※ This function is effectively an alias for `Math.asinh`.
202 *
203 * ☡ This function does not allow big·int arguments.
204 */
205 export const arcsinh = createArrowFunction(
206 Math.asinh,
207 { name: "arcsinh" },
208 );
209
210 /**
211 * Returns the arctan of the provided value.
212 *
213 * ※ This function is effectively an alias for `Math.atan`.
214 *
215 * ☡ This function does not allow big·int arguments.
216 */
217 export const arctan = createArrowFunction(
218 Math.atan,
219 { name: "arctan" },
220 );
221
222 export const {
223 /**
224 * Returns the arctangent of the dividend of the provided values.
225 *
226 * ※ Unlike `Math.atan2`, this function can take big·int arguments.
227 * However, the result will always be a number.
228 */
229 arctan2,
230
231 /**
232 * Returns the number of leading zeroes in the 32‐bit representation
233 * of the provided value.
234 *
235 * ※ Unlike `Math.clz32`, this function accepts either number or
236 * big·int values.
237 */
238 clz32,
239
240 /**
241 * Returns the 32‐bit float which best approximate the provided
242 * value.
243 *
244 * ※ Unlike `Math.fround`, this function can take big·int arguments.
245 * However, the result will always be a number.
246 */
247 toFloat32,
248 } = (() => {
249 const { atan2, fround, clz32 } = Math;
250 return {
251 arctan2: (y, x) => atan2(toNumber(y), toNumber(x)),
252 clz32: ($) => {
253 const n = toNumeric($);
254 return clz32(
255 typeof n === "bigint"
256 ? toNumber(toUnsignedIntegralNumeric(32, n))
257 : n,
258 );
259 },
260 toFloat32: ($) => fround(toNumber($)),
261 };
262 })();
263
264 /**
265 * Returns the arctanh of the provided value.
266 *
267 * ※ This function is effectively an alias for `Math.atanh`.
268 *
269 * ☡ This function does not allow big·int arguments.
270 */
271 export const arctanh = createArrowFunction(
272 Math.atanh,
273 { name: "arctanh" },
274 );
275
276 /**
277 * Returns the cube root of the provided value.
278 *
279 * ※ This function is effectively an alias for `Math.cbrt`.
280 *
281 * ☡ This function does not allow big·int arguments.
282 */
283 export const cbrt = createArrowFunction(Math.cbrt);
284
285 /**
286 * Returns the ceiling of the provided value.
287 *
288 * ※ This function is effectively an alias for `Math.ceil`.
289 *
290 * ☡ This function does not allow big·int arguments.
291 */
292 export const ceil = createArrowFunction(Math.ceil);
293
294 /**
295 * Returns the cos of the provided value.
296 *
297 * ※ This function is effectively an alias for `Math.cos`.
298 *
299 * ☡ This function does not allow big·int arguments.
300 */
301 export const cos = createArrowFunction(Math.cos);
302
303 /**
304 * Returns the cosh of the provided value.
305 *
306 * ※ This function is effectively an alias for `Math.cosh`.
307 *
308 * ☡ This function does not allow big·int arguments.
309 */
310 export const cosh = createArrowFunction(Math.cosh);
311
312 /**
313 * Returns the Euler number raised to the provided value.
314 *
315 * ※ This function is effectively an alias for `Math.exp`.
316 *
317 * ☡ This function does not allow big·int arguments.
318 */
319 export const exp = createArrowFunction(Math.exp);
320
321 /**
322 * Returns the Euler number raised to the provided value, minus one.
323 *
324 * ※ This function is effectively an alias for `Math.expm1`.
325 *
326 * ☡ This function does not allow big·int arguments.
327 */
328 export const expm1 = createArrowFunction(Math.expm1);
329
330 /**
331 * Returns the floor of the provided value.
332 *
333 * ※ This function is effectively an alias for `Math.floor`.
334 *
335 * ☡ This function does not allow big·int arguments.
336 */
337 export const floor = createArrowFunction(Math.floor);
338
339 /**
340 * Returns the square root of the sum of the squares of the provided
341 * arguments.
342 *
343 * ※ This function is effectively an alias for `Math.hypot`.
344 *
345 * ☡ This function does not allow big·int arguments.
346 */
347 export const hypot = createArrowFunction(Math.hypot);
348
349 /**
350 * Returns whether the provided value is a finite number.
351 *
352 * ※ This function is effectively an alias for `Number.isFinite`.
353 */
354 export const isFiniteNumber = createArrowFunction(
355 Number.isFinite,
356 { name: "isFiniteNumber" },
357 );
358
359 /**
360 * Returns whether the provided value is an integral number.
361 *
362 * ※ This function is effectively an alias for `Number.isInteger`.
363 */
364 export const isIntegralNumber = createArrowFunction(
365 Number.isInteger,
366 { name: "isIntegralNumber" },
367 );
368
369 /**
370 * Returns whether the provided value is nan.
371 *
372 * ※ This function is effectively an alias for `Number.isNaN`.
373 */
374 export const isNan = createArrowFunction(
375 Number.isNaN,
376 { name: "isNan" },
377 );
378
379 /**
380 * Returns whether the provided value is a safe integral number.
381 *
382 * ※ This function is effectively an alias for `Number.isSafeInteger`.
383 */
384 export const isSafeIntegralNumber = createArrowFunction(
385 Number.isSafeInteger,
386 { name: "isSafeIntegralNumber" },
387 );
388
389 /**
390 * Returns the ln of the provided value.
391 *
392 * ※ This function is effectively an alias for `Math.log`.
393 *
394 * ☡ This function does not allow big·int arguments.
395 */
396 export const ln = createArrowFunction(Math.log, { name: "ln" });
397
398 /**
399 * Returns the ln of one plus the provided value.
400 *
401 * ※ This function is effectively an alias for `Math.log1p`.
402 *
403 * ☡ This function does not allow big·int arguments.
404 */
405 export const ln1p = createArrowFunction(Math.log1p, { name: "ln1p" });
406
407 /**
408 * Returns the log10 of the provided value.
409 *
410 * ※ This function is effectively an alias for `Math.log10`.
411 *
412 * ☡ This function does not allow big·int arguments.
413 */
414 export const log10 = createArrowFunction(Math.log10);
415
416 /**
417 * Returns the log2 of the provided value.
418 *
419 * ※ This function is effectively an alias for `Math.log2`.
420 *
421 * ☡ This function does not allow big·int arguments.
422 */
423 export const log2 = createArrowFunction(Math.log2);
424
425 /**
426 * Returns the highest value of the provided arguments, or negative
427 * infinity if no argument is provided.
428 *
429 * ※ Unlike `Math.max`, this function accepts either number or big·int
430 * values. All values must be of the same type, or this function will
431 * throw an error.
432 *
433 * ☡ If no argument is supplied, the result will be a number, not a
434 * big·int.
435 */
436 export const max = Object.defineProperties((...$s) => {
437 let highest = undefined;
438 for (let i = 0; i < $s.length; ++i) {
439 // Iterate over all the numbers.
440 const number = toNumeric($s[i]);
441 if (highest === undefined) {
442 // The current number is the first one.
443 if (isNan(number)) {
444 // The current number is nan.
445 return NAN;
446 } else {
447 // The current number is not nan.
448 highest = number;
449 }
450 } else {
451 if (typeof highest !== typeof number) {
452 // The type of the current number and the lowest number don’t
453 // match.
454 throw new TypeError("Piscēs: Type mismatch.");
455 } else if (isNan(number)) {
456 // The current number is nan.
457 return NAN;
458 } else if (sameValue(number, 0) && sameValue(highest, -0)) {
459 // The current number is +0 and the highest number is -0.
460 highest = 0;
461 } else if (highest === undefined || number > highest) {
462 // The current number is greater than the highest number.
463 highest = number;
464 } else {
465 // The current number is less than or equal to the lowest
466 // number.
467 /* do nothing */
468 }
469 }
470 }
471 return highest ?? NEGATIVE_INFINITY;
472 }, { name: { value: "max" }, length: { value: 2 } });
473
474 /**
475 * Returns the lowest value of the provided arguments, or positive
476 * infinity if no argument is provided.
477 *
478 * ※ Unlike `Math.min`, this function accepts either number or big·int
479 * values. All values must be of the same type, or this function will
480 * throw an error.
481 *
482 * ☡ If no argument is supplied, the result will be a number, not a
483 * big·int.
484 */
485 export const min = Object.defineProperties((...$s) => {
486 let lowest = undefined;
487 for (let i = 0; i < $s.length; ++i) {
488 // Iterate over all the numbers.
489 const number = toNumeric($s[i]);
490 if (lowest === undefined) {
491 // The current number is the first one.
492 if (isNan(number)) {
493 // The current number is nan.
494 return NAN;
495 } else {
496 // The current number is not nan.
497 lowest = number;
498 }
499 } else {
500 // The current number is not the first one.
501 if (typeof lowest !== typeof number) {
502 // The type of the current number and the lowest number don’t
503 // match.
504 throw new TypeError("Piscēs: Type mismatch.");
505 } else if (isNan(number)) {
506 // The current number is nan.
507 return NAN;
508 } else if (sameValue(number, -0) && sameValue(lowest, 0)) {
509 // The current number is -0 and the lowest number is +0.
510 lowest = -0;
511 } else if (number < lowest) {
512 // The current number is less than the lowest number.
513 lowest = number;
514 } else {
515 // The current number is greater than or equal to the lowest
516 // number.
517 /* do nothing */
518 }
519 }
520 }
521 return lowest ?? POSITIVE_INFINITY;
522 }, { name: { value: "min" }, length: { value: 2 } });
523
524 /**
525 * Returns a pseudo·random value in the range [0, 1).
526 *
527 * ※ This function is effectively an alias for `Math.random`.
528 */
529 export const rand = createArrowFunction(
530 Math.random,
531 { name: "rand" },
532 );
533
534 /**
535 * Returns the round of the provided value.
536 *
537 * ※ This function is effectively an alias for `Math.round`.
538 *
539 * ☡ This function does not allow big·int arguments.
540 */
541 export const round = createArrowFunction(Math.round);
542
543 /**
544 * Returns a unit value with the same sign as the provided value, or
545 * the provided value itself if it is not a number or (potentially
546 * signed) zero.
547 *
548 * For big·ints, the return value of this function is 0n if the
549 * provided value is 0n, −1n if the provided value is negative, and +1n
550 * otherwise.
551 *
552 * For numbers, the return value is nan, −0, or +0 if the provided
553 * value is nan, −0, or +0, respectively, and −1 if the provided value
554 * is negative and +1 if the provided value is positive otherwise. Note
555 * that positive and negative infinity will return +1 and −1
556 * respectively.
557 *
558 * ※ Unlike `Math.sign`, this function accepts either number or
559 * big·int values.
560 */
561 export const sgn = ($) => {
562 const n = toNumeric($);
563 return typeof n === "bigint"
564 ? n === 0n ? 0n : n < 0n ? -1n : 1n
565 : isNan(n) || n === 0
566 ? n
567 //deno-lint-ignore no-compare-neg-zero
568 : n < -0
569 ? -1
570 : 1;
571 };
572
573 /**
574 * Returns the sin of the provided value.
575 *
576 * ※ This function is effectively an alias for `Math.sin`.
577 *
578 * ☡ This function does not allow big·int arguments.
579 */
580 export const sin = createArrowFunction(Math.sin);
581
582 /**
583 * Returns the sinh of the provided value.
584 *
585 * ※ This function is effectively an alias for `Math.sinh`.
586 *
587 * ☡ This function does not allow big·int arguments.
588 */
589 export const sinh = createArrowFunction(Math.sinh);
590
591 /**
592 * Returns the square root of the provided value.
593 *
594 * ※ This function is effectively an alias for `Math.sqrt`.
595 *
596 * ☡ This function does not allow big·int arguments.
597 */
598 export const sqrt = createArrowFunction(Math.sqrt);
599
600 /**
601 * Returns the tan of the provided value.
602 *
603 * ※ This function is effectively an alias for `Math.tan`.
604 *
605 * ☡ This function does not allow big·int arguments.
606 */
607 export const tan = createArrowFunction(Math.tan);
608
609 /**
610 * Returns the tanh of the provided value.
611 *
612 * ※ This function is effectively an alias for `Math.tanh`.
613 *
614 * ☡ This function does not allow big·int arguments.
615 */
616 export const tanh = createArrowFunction(Math.tanh);
617
618 /**
619 * Returns the result of converting the provided value to a big·int.
620 *
621 * ※ This method is safe to use with numbers.
622 *
623 * ※ This is effectively an alias for `BigInt`.
624 */
625 export const { toBigInt } = (() => {
626 const makeBigInt = BigInt;
627 return { toBigInt: ($) => makeBigInt($) };
628 })();
629
630 export const {
631 /**
632 * Returns the result of converting the provided value to an
633 * exponential notation string.
634 *
635 * If a second argument is provided, it gives the number of
636 * fractional digits to use in the mantissa. Otherwise, the smallest
637 * number which does not result in a reduction in precision is used.
638 *
639 * ※ This method is safe to use with big·ints.
640 */
641 toExponentialNotation,
642
643 /**
644 * Returns the result of converting the provided value to a fixed
645 * decimal notation string with the provided number of fractional
646 * digits.
647 *
648 * ※ This method is safe to use with big·ints.
649 */
650 toFixedDecimalNotation,
651 } = (() => {
652 const {
653 toExponential: numberToExponential,
654 toFixed: numberToFixed,
655 } = Number.prototype;
656 const { toString: bigintToString } = BigInt.prototype;
657 return {
658 toExponentialNotation: ($, fractionDigits) => {
659 const n = toNumeric($);
660 const f = toIntegralNumberOrInfinity(fractionDigits);
661 if (!isFiniteNumber(f) || f < 0 || f > 100) {
662 throw new RangeError(
663 `Piscēs: The number of fractional digits must be a finite number between 0 and 100 inclusive; got: ${f}.`,
664 );
665 } else {
666 if (typeof n === "number") {
667 return call(
668 numberToExponential,
669 n,
670 [fractionDigits === undefined ? fractionDigits : f],
671 );
672 } else {
673 const digits = call(bigintToString, n, [10]);
674 const { length } = digits;
675 if (fractionDigits === undefined) {
676 return length === 1
677 ? `${digits[0]}e+0`
678 : `${digits[0]}.${substring(digits, 1)}e+${length - 1}`;
679 } else if (f === 0) {
680 return `${digits[0]}e+0`;
681 } else {
682 const fractionalPart = toString(
683 round(
684 +stringCatenate(
685 stringPadEnd(substring(digits, 1, f + 1), f, "0"),
686 ".",
687 digits[f + 1] || "0",
688 ),
689 ),
690 );
691 return `${digits[0]}.${fractionalPart}e+${length - 1}`;
692 }
693 }
694 }
695 },
696 toFixedDecimalNotation: ($, fractionDigits) => {
697 const f = toIntegralNumberOrInfinity(fractionDigits);
698 if (!isFiniteNumber(f) || f < 0 || f > 100) {
699 throw new RangeError(
700 `Piscēs: The number of fractional digits must be a finite number between 0 and 100 inclusive; got: ${f}.`,
701 );
702 } else {
703 const n = toNumeric($);
704 if (typeof n === "number") {
705 return call(numberToFixed, n, [f]);
706 } else {
707 const digits = call(bigintToString, n, [10]);
708 return f === 0
709 ? digits
710 : `${digits}.${stringRepeat("0", f)}`;
711 }
712 }
713 },
714 };
715 })();
716
717 /**
718 * Returns the result of converting the provided number to an integral
719 * number.
720 *
721 * ※ This function will never return negative zero.
722 */
723 export const toIntegralNumber = ($) => {
724 const n = toIntegralNumberOrInfinity($);
725 return !isFiniteNumber(n) || n == 0 ? 0 : n;
726 };
727
728 /**
729 * Returns the result of converting the provided number to an integer
730 * or infinity.
731 *
732 * ※ Unlike the ToIntegerOrInfinity function defined in the Ecmascript
733 * specification, this function is safe to use with big·ints. However,
734 * the result will always be a number.
735 *
736 * ※ This function will never return negative zero.
737 */
738 export const toIntegralNumberOrInfinity = ($) => {
739 const integer = trunc(toNumber($));
740 if (isNan(integer) || integer == 0) {
741 // The provided value truncs to nan or (positive or negative) zero.
742 return 0;
743 } else if (integer == POSITIVE_INFINITY) {
744 // The provided value truncs to positive infinity.
745 return POSITIVE_INFINITY;
746 } else if (integer == NEGATIVE_INFINITY) {
747 // The provided value truncs to negative infinity.
748 return NEGATIVE_INFINITY;
749 } else {
750 // The provided value truncs to an integer.
751 return integer;
752 }
753 };
754
755 /**
756 * Returns the result of converting the provided value to a number.
757 *
758 * ※ This function is safe to use with big·ints.
759 *
760 * ※ This is effectively a nonconstructible version of the `Number`
761 * constructor.
762 */
763 export const { toNumber } = (() => {
764 const makeNumber = Number;
765 return { toNumber: ($) => makeNumber($) };
766 })();
767
768 /**
769 * Returns the result of converting the provided value to a number or
770 * big·int.
771 *
772 * ※ If the result of converting the provided value to a primitive is
773 * not a big·int, this function will return a number.
774 */
775 export const toNumeric = ($) => {
776 const primValue = toPrimitive($, "number");
777 return typeof primValue === "bigint" ? primValue : +primValue;
778 };
779
780 export const {
781 /**
782 * Returns the result of converting the provided value to fit within
783 * the provided number of bits as a signed integer.
784 *
785 * ※ Unlike `BigInt.asIntN`, this function accepts both big·int and
786 * number values.
787 *
788 * ☡ The first argument, the number of bits, must be a number.
789 */
790 toSignedIntegralNumeric,
791
792 /**
793 * Returns the result of converting the provided value to fit within
794 * the provided number of bits as an unsigned integer.
795 *
796 * ※ Unlike `BigInt.asUintN`, this function accepts both big·int and
797 * number values.
798 *
799 * ☡ The first argument, the number of bits, must be a number.
800 */
801 toUnsignedIntegralNumeric,
802 } = (() => {
803 const { asIntN, asUintN } = BigInt;
804 return {
805 toSignedIntegralNumeric: (n, $) => {
806 const prim = toPrimitive($);
807 if (typeof prim === "bigint") {
808 // The primitive value is a big·int.
809 return asIntN(n, prim);
810 } else {
811 // The primitive value is not a big·int.
812 const int = trunc(prim);
813 if (!isFiniteNumber(int) || int == 0) {
814 // The truncated value is zero or not finite.
815 return 0;
816 } else {
817 // The truncated value is finite.
818 return toNumber(asIntN(n, toBigInt(int)));
819 }
820 }
821 },
822 toUnsignedIntegralNumeric: (n, $) => {
823 const prim = toPrimitive($);
824 if (typeof prim === "bigint") {
825 // The primitive value is a big·int.
826 return asUintN(n, prim);
827 } else {
828 // The primitive value is not a big·int.
829 const int = trunc(prim);
830 if (!isFiniteNumber(int) || int == 0) {
831 // The truncated value is zero or not finite.
832 return 0;
833 } else {
834 // The truncated value is finite.
835 return toNumber(asUintN(n, toBigInt(int)));
836 }
837 }
838 },
839 };
840 })();
841
842 /**
843 * Returns the trunc of the provided value.
844 *
845 * ※ This function is effectively an alias for `Math.trunc`.
846 *
847 * ☡ This function does not allow big·int arguments.
848 */
849 export const trunc = createArrowFunction(Math.trunc);
This page took 0.213639 seconds and 5 git commands to generate.