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