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