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