X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/10f5f13665b3454942bc8b1c49c6d4c46a5ac560..83f6aae0d1b8181dc2b0c6ccdba9f2fe2fdba3e6:/numeric.js diff --git a/numeric.js b/numeric.js index 1d0a543..5a3d6ea 100644 --- a/numeric.js +++ b/numeric.js @@ -1,61 +1,69 @@ // ♓🌟 Piscēs ∷ numeric.js // ==================================================================== // -// Copyright © 2022 Lady [@ Lady’s Computer]. +// Copyright © 2022–2023 Lady [@ Lady’s Computer]. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at . +import { call } from "./function.js"; +import { + stringCatenate, + stringPadEnd, + stringRepeat, + substring, + toString, +} from "./string.js"; import { sameValue, toPrimitive } from "./value.js"; export const { /** * ln(10). * - * ※ This is an alias for Math.LN10. + * ※ This is an alias for `Math.LN10`. */ LN10, /** * ln(2). * - * ※ This is an alias for Math.LN2. + * ※ This is an alias for `Math.LN2`. */ LN2, /** * log10(ℇ). * - * ※ This is an alias for Math.LOG10E. + * ※ This is an alias for `Math.LOG10E`. */ LOG10E: LOG10ℇ, /** * log2(ℇ). * - * ※ This is an alias for Math.LOG2E. + * ※ This is an alias for `Math.LOG2E`. */ LOG2E: LOG2ℇ, /** - * sqrt(.5). + * sqrt(½). * - * ※ This is an alias for Math.SQRT1_2. + * ※ This is an alias for `Math.SQRT1_2`. */ SQRT1_2: RECIPROCAL_SQRT2, /** * sqrt(2). * - * ※ This is an alias for Math.SQRT2. + * ※ This is an alias for `Math.SQRT2`. */ SQRT2, /** * Returns the arccos of the provided value. * - * ※ This is an alias for Math.acos. + * ※ This is an alias for `Math.acos`. * * ☡ This function does not allow big·int arguments. */ @@ -64,7 +72,7 @@ export const { /** * Returns the arccosh of the provided value. * - * ※ This is an alias for Math.acosh. + * ※ This is an alias for `Math.acosh`. * * ☡ This function does not allow big·int arguments. */ @@ -73,7 +81,7 @@ export const { /** * Returns the arcsin of the provided value. * - * ※ This is an alias for Math.asin. + * ※ This is an alias for `Math.asin`. * * ☡ This function does not allow big·int arguments. */ @@ -82,7 +90,7 @@ export const { /** * Returns the arcsinh of the provided value. * - * ※ This is an alias for Math.asinh. + * ※ This is an alias for `Math.asinh`. * * ☡ This function does not allow big·int arguments. */ @@ -91,7 +99,7 @@ export const { /** * Returns the arctan of the provided value. * - * ※ This is an alias for Math.atan. + * ※ This is an alias for `Math.atan`. * * ☡ This function does not allow big·int arguments. */ @@ -100,7 +108,7 @@ export const { /** * Returns the arctanh of the provided value. * - * ※ This is an alias for Math.atanh. + * ※ This is an alias for `Math.atanh`. * * ☡ This function does not allow big·int arguments. */ @@ -109,7 +117,7 @@ export const { /** * Returns the cube root of the provided value. * - * ※ This is an alias for Math.cbrt. + * ※ This is an alias for `Math.cbrt`. * * ☡ This function does not allow big·int arguments. */ @@ -118,7 +126,7 @@ export const { /** * Returns the ceiling of the provided value. * - * ※ This is an alias for Math.ceil. + * ※ This is an alias for `Math.ceil`. * * ☡ This function does not allow big·int arguments. */ @@ -127,7 +135,7 @@ export const { /** * Returns the cos of the provided value. * - * ※ This is an alias for Math.cos. + * ※ This is an alias for `Math.cos`. * * ☡ This function does not allow big·int arguments. */ @@ -136,7 +144,7 @@ export const { /** * Returns the cosh of the provided value. * - * ※ This is an alias for Math.cosh. + * ※ This is an alias for `Math.cosh`. * * ☡ This function does not allow big·int arguments. */ @@ -145,7 +153,7 @@ export const { /** * Returns the Euler number raised to the provided value. * - * ※ This is an alias for Math.exp. + * ※ This is an alias for `Math.exp`. * * ☡ This function does not allow big·int arguments. */ @@ -154,7 +162,7 @@ export const { /** * Returns the Euler number raised to the provided value, minus one. * - * ※ This is an alias for Math.expm1. + * ※ This is an alias for `Math.expm1`. * * ☡ This function does not allow big·int arguments. */ @@ -163,7 +171,7 @@ export const { /** * Returns the floor of the provided value. * - * ※ This is an alias for Math.floor. + * ※ This is an alias for `Math.floor`. * * ☡ This function does not allow big·int arguments. */ @@ -173,7 +181,7 @@ export const { * Returns the square root of the sum of the squares of the provided * arguments. * - * ※ This is an alias for Math.hypot. + * ※ This is an alias for `Math.hypot`. * * ☡ This function does not allow big·int arguments. */ @@ -182,7 +190,7 @@ export const { /** * Returns the ln of the provided value. * - * ※ This is an alias for Math.log. + * ※ This is an alias for `Math.log`. * * ☡ This function does not allow big·int arguments. */ @@ -191,7 +199,7 @@ export const { /** * Returns the log10 of the provided value. * - * ※ This is an alias for Math.log10. + * ※ This is an alias for `Math.log10`. * * ☡ This function does not allow big·int arguments. */ @@ -200,7 +208,7 @@ export const { /** * Returns the ln of one plus the provided value. * - * ※ This is an alias for Math.log1p. + * ※ This is an alias for `Math.log1p`. * * ☡ This function does not allow big·int arguments. */ @@ -209,7 +217,7 @@ export const { /** * Returns the log2 of the provided value. * - * ※ This is an alias for Math.log2. + * ※ This is an alias for `Math.log2`. * * ☡ This function does not allow big·int arguments. */ @@ -218,14 +226,14 @@ export const { /** * Returns a pseudo·random value in the range [0, 1). * - * ※ This is an alias for Math.random. + * ※ This is an alias for `Math.random`. */ random: rand, /** * Returns the round of the provided value. * - * ※ This is an alias for Math.round. + * ※ This is an alias for `Math.round`. * * ☡ This function does not allow big·int arguments. */ @@ -234,7 +242,7 @@ export const { /** * Returns the sinh of the provided value. * - * ※ This is an alias for Math.sinh. + * ※ This is an alias for `Math.sinh`. * * ☡ This function does not allow big·int arguments. */ @@ -243,7 +251,7 @@ export const { /** * Returns the square root of the provided value. * - * ※ This is an alias for Math.sqrt. + * ※ This is an alias for `Math.sqrt`. * * ☡ This function does not allow big·int arguments. */ @@ -252,7 +260,7 @@ export const { /** * Returns the tan of the provided value. * - * ※ This is an alias for Math.tan. + * ※ This is an alias for `Math.tan`. * * ☡ This function does not allow big·int arguments. */ @@ -261,7 +269,7 @@ export const { /** * Returns the tanh of the provided value. * - * ※ This is an alias for Math.tanh. + * ※ This is an alias for `Math.tanh`. * * ☡ This function does not allow big·int arguments. */ @@ -270,7 +278,7 @@ export const { /** * Returns the trunc of the provided value. * - * ※ This is an alias for Math.trunc. + * ※ This is an alias for `Math.trunc`. * * ☡ This function does not allow big·int arguments. */ @@ -279,14 +287,14 @@ export const { /** * The mathematical constant π. * - * ※ This is an alias for Math.PI. + * ※ This is an alias for `Math.PI`. */ PI: Π, /** * The Euler number. * - * ※ This is an alias for Math.E. + * ※ This is an alias for `Math.E`. */ E: ℇ, } = Math; @@ -295,92 +303,98 @@ export const { /** * The largest number value less than infinity. * - * ※ This is an alias for Number.MAX_VALUE. + * ※ This is an alias for `Number.MAX_VALUE`. */ MAX_VALUE: MAXIMUM_NUMBER, /** * 2**53 - 1. * - * ※ This is an alias for Number.MAX_SAFE_INTEGER. + * ※ This is an alias for `Number.MAX_SAFE_INTEGER`. */ MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER, /** * The smallest number value greater than negative infinity. * - * ※ This is an alias for Number.MIN_VALUE. + * ※ This is an alias for `Number.MIN_VALUE`. */ MIN_VALUE: MINIMUM_NUMBER, /** * -(2**53 - 1). * - * ※ This is an alias for Number.MIN_SAFE_INTEGER. + * ※ This is an alias for `Number.MIN_SAFE_INTEGER`. */ MIN_SAFE_INTEGER: MINIMUM_SAFE_INTEGRAL_NUMBER, /** * Negative infinity. * - * ※ This is an alias for Number.NEGATIVE_INFINITY. + * ※ This is an alias for `Number.NEGATIVE_INFINITY`. */ NEGATIVE_INFINITY, /** * Nan. * - * ※ This is an alias for Number.NaN. + * ※ This is an alias for `Number.NaN`. */ NaN: NAN, /** * Positive infinity. * - * ※ This is an alias for Number.POSITIVE_INFINITY. + * ※ This is an alias for `Number.POSITIVE_INFINITY`. */ POSITIVE_INFINITY, /** * The difference between 1 and the smallest number greater than 1. * - * ※ This is an alias for Number.EPSILON. + * ※ This is an alias for `Number.EPSILON`. */ EPSILON: Ε, /** * Returns whether the provided value is a finite number. * - * ※ This is an alias for Number.isFinite. + * ※ This is an alias for `Number.isFinite`. */ isFinite: isFiniteNumber, /** * Returns whether the provided value is an integral number. * - * ※ This is an alias for Number.isInteger. + * ※ This is an alias for `Number.isInteger`. */ isInteger: isIntegralNumber, /** * Returns whether the provided value is nan. * - * ※ This is an alias for Number.isNaN. + * ※ This is an alias for `Number.isNaN`. */ isNaN: isNan, /** * Returns whether the provided value is a safe integral number. * - * ※ This is an alias for Number.isSafeInteger. + * ※ This is an alias for `Number.isSafeInteger`. */ isSafeInteger: isSafeIntegralNumber, } = Number; +/** Positive zero. */ +export const POSITIVE_ZERO = 0; + +/** Negative zero. */ +export const NEGATIVE_ZERO = -0; + /** * Returns the magnitude (absolute value) of the provided value. * - * ※ Unlike Math.abs, this function can take big·int arguments. + * ※ Unlike `Math.abs`, this function can take big·int arguments. */ export const abs = ($) => { const n = toNumeric($); @@ -401,17 +415,17 @@ export const { /** * Returns the arctangent of the dividend of the provided values. * - * ※ Unlike Math.atan2, this function can take big·int arguments. + * ※ Unlike `Math.atan2`, this function can take big·int arguments. * However, the result will always be a number. */ atan2, /** - * Returns the number of leading zeroes in the 32‐bit representation of - * the provided value. + * Returns the number of leading zeroes in the 32‐bit representation + * of the provided value. * - * ※ Unlike Math.clz32, this function accepts either number or big·int - * values. + * ※ Unlike `Math.clz32`, this function accepts either number or + * big·int values. */ clz32, @@ -419,7 +433,7 @@ export const { * Returns the 32‐bit float which best approximate the provided * value. * - * ※ Unlike Math.fround, this function can take big·int arguments. + * ※ Unlike `Math.fround`, this function can take big·int arguments. * However, the result will always be a number. */ toFloat32, @@ -430,7 +444,9 @@ export const { clz32: ($) => { const n = toNumeric($); return clz32( - typeof n === "bigint" ? toNumber(toUintN(32, n)) : n, + typeof n === "bigint" + ? toNumber(toUnsignedIntegralNumeric(32, n)) + : n, ); }, toFloat32: ($) => fround(toNumber($)), @@ -441,7 +457,7 @@ export const { * Returns the highest value of the provided arguments, or negative * infinity if no argument is provided. * - * ※ Unlike Math.max, this function accepts either number or big·int + * ※ Unlike `Math.max`, this function accepts either number or big·int * values. All values must be of the same type, or this function will * throw an error. * @@ -490,7 +506,7 @@ export const max = (...$s) => { * Returns the lowest value of the provided arguments, or positive * infinity if no argument is provided. * - * ※ Unlike Math.min, this function accepts either number or big·int + * ※ Unlike `Math.min`, this function accepts either number or big·int * values. All values must be of the same type, or this function will * throw an error. * @@ -542,17 +558,17 @@ export const min = (...$s) => { * signed) zero. * * For big·ints, the return value of this function is 0n if the - * provided value is 0n, -1n if the provided value is negative, and +1n + * provided value is 0n, −1n if the provided value is negative, and +1n * otherwise. * - * For numbers, the return value is nan, -0, or +0 if the provided - * value is nan, -0, or +0, respectively, and -1 if the provided value + * For numbers, the return value is nan, −0, or +0 if the provided + * value is nan, −0, or +0, respectively, and −1 if the provided value * is negative and +1 if the provided value is positive otherwise. Note - * that positive and negative infinity will return +1 and -1 + * that positive and negative infinity will return +1 and −1 * respectively. * - * ※ Unlike Math.sign, this function accepts either number or big·int - * values. + * ※ Unlike `Math.sign`, this function accepts either number or + * big·int values. */ export const sgn = ($) => { const n = toNumeric($); @@ -560,10 +576,10 @@ export const sgn = ($) => { ? n === 0n ? 0n : n < 0n ? -1n : 1n : isNan(n) || n === 0 ? n - : //deno-lint-ignore no-compare-neg-zero - n < -0 - ? -1 - : 1; + //deno-lint-ignore no-compare-neg-zero + : n < -0 + ? -1 + : 1; }; /** @@ -571,7 +587,7 @@ export const sgn = ($) => { * * ※ This method is safe to use with numbers. * - * ※ This is effectively an alias for BigInt. + * ※ This is effectively an alias for `BigInt`. */ export const { toBigInt } = (() => { const makeBigInt = BigInt; @@ -580,74 +596,114 @@ export const { toBigInt } = (() => { export const { /** - * Returns the result of converting the provided value to fit within - * the provided number of bits as a signed integer. + * Returns the result of converting the provided value to an + * exponential notation string. * - * ※ Unlike BigInt.asIntN, this function accepts both big·int and - * number values. + * If a second argument is provided, it gives the number of + * fractional digits to use in the mantissa. Otherwise, the smallest + * number which does not result in a reduction in precision is used. * - * ☡ The first argument, the number of bits, must be a number. + * ※ This method is safe to use with big·ints. */ - toIntN, + toExponentialNotation, /** - * Returns the result of converting the provided value to fit within - * the provided number of bits as an unsigned integer. - * - * ※ Unlike BigInt.asUintN, this function accepts both big·int and - * number values. + * Returns the result of converting the provided value to a fixed + * decimal notation string with the provided number of fractional + * digits. * - * ☡ The first argument, the number of bits, must be a number. + * ※ This method is safe to use with big·ints. */ - toUintN, + toFixedDecimalNotation, } = (() => { - const { asIntN, asUintN } = BigInt; + const { + toExponential: numberToExponential, + toFixed: numberToFixed, + } = Number.prototype; + const { toString: bigintToString } = BigInt.prototype; return { - toIntN: (n, $) => { - const prim = toPrimitive($); - if (typeof prim === "bigint") { - // The primitive value is a big·int. - return asIntN(n, prim); + toExponentialNotation: ($, fractionDigits) => { + const n = toNumeric($); + const f = toIntegralNumberOrInfinity(fractionDigits); + if (!isFiniteNumber(f) || f < 0 || f > 100) { + throw new RangeError( + `Piscēs: The number of fractional digits must be a finite number between 0 and 100 inclusive; got: ${f}.`, + ); } else { - // The primitive value is not a big·int. - const int = trunc(prim); - if (!isFiniteNumber(int) || int == 0) { - // The truncated value is zero or not finite. - return 0; + if (typeof n === "number") { + return call( + numberToExponential, + n, + [fractionDigits === undefined ? fractionDigits : f], + ); } else { - // The truncated value is finite. - return toNumber(asIntN(n, toBigInt(int))); + const digits = call(bigintToString, n, [10]); + const { length } = digits; + if (fractionDigits === undefined) { + return length === 1 + ? `${digits[0]}e+0` + : `${digits[0]}.${substring(digits, 1)}e+${length - 1}`; + } else if (f === 0) { + return `${digits[0]}e+0`; + } else { + const fractionalPart = toString( + round( + +stringCatenate( + stringPadEnd(substring(digits, 1, f + 1), f, "0"), + ".", + digits[f + 1] || "0", + ), + ), + ); + return `${digits[0]}.${fractionalPart}e+${length - 1}`; + } } } }, - toUintN: (n, $) => { - const prim = toPrimitive($); - if (typeof prim === "bigint") { - // The primitive value is a big·int. - return asUintN(n, prim); + toFixedDecimalNotation: ($, fractionDigits) => { + const f = toIntegralNumberOrInfinity(fractionDigits); + if (!isFiniteNumber(f) || f < 0 || f > 100) { + throw new RangeError( + `Piscēs: The number of fractional digits must be a finite number between 0 and 100 inclusive; got: ${f}.`, + ); } else { - // The primitive value is not a big·int. - const int = trunc(prim); - if (!isFiniteNumber(int) || int == 0) { - // The truncated value is zero or not finite. - return 0; + const n = toNumeric($); + if (typeof n === "number") { + return call(numberToFixed, n, [f]); } else { - // The truncated value is finite. - return toNumber(asUintN(n, toBigInt(int))); + const digits = call(bigintToString, n, [10]); + return f === 0 + ? digits + : `${digits}.${stringRepeat("0", f)}`; } } }, }; })(); +/** + * Returns the result of converting the provided number to an integral + * number. + * + * ※ This function will never return negative zero. + */ +export const toIntegralNumber = ($) => { + const n = toIntegralNumberOrInfinity($); + return !isFiniteNumber(n) || n == 0 ? 0 : n; +}; + /** * Returns the result of converting the provided number to an integer * or infinity. * - * ☡ This function does not allow big·int arguments. + * ※ Unlike the ToIntegerOrInfinity function defined in the Ecmascript + * specification, this function is safe to use with big·ints. However, + * the result will always be a number. + * + * ※ This function will never return negative zero. */ -export const toIntegerOrInfinity = ($) => { - const integer = trunc($); +export const toIntegralNumberOrInfinity = ($) => { + const integer = trunc(toNumber($)); if (isNan(integer) || integer == 0) { // The provided value truncs to nan or (positive or negative) zero. return 0; @@ -666,9 +722,9 @@ export const toIntegerOrInfinity = ($) => { /** * Returns the result of converting the provided value to a number. * - * ※ This method is safe to use with big·ints. + * ※ This function is safe to use with big·ints. * - * ※ This is effectively a nonconstructible version of the Number + * ※ This is effectively a nonconstructible version of the `Number` * constructor. */ export const { toNumber } = (() => { @@ -687,3 +743,65 @@ export const toNumeric = ($) => { const primValue = toPrimitive($, "number"); return typeof primValue === "bigint" ? primValue : +primValue; }; + +export const { + /** + * Returns the result of converting the provided value to fit within + * the provided number of bits as a signed integer. + * + * ※ Unlike `BigInt.asIntN`, this function accepts both big·int and + * number values. + * + * ☡ The first argument, the number of bits, must be a number. + */ + toSignedIntegralNumeric, + + /** + * Returns the result of converting the provided value to fit within + * the provided number of bits as an unsigned integer. + * + * ※ Unlike `BigInt.asUintN`, this function accepts both big·int and + * number values. + * + * ☡ The first argument, the number of bits, must be a number. + */ + toUnsignedIntegralNumeric, +} = (() => { + const { asIntN, asUintN } = BigInt; + return { + toSignedIntegralNumeric: (n, $) => { + const prim = toPrimitive($); + if (typeof prim === "bigint") { + // The primitive value is a big·int. + return asIntN(n, prim); + } else { + // The primitive value is not a big·int. + const int = trunc(prim); + if (!isFiniteNumber(int) || int == 0) { + // The truncated value is zero or not finite. + return 0; + } else { + // The truncated value is finite. + return toNumber(asIntN(n, toBigInt(int))); + } + } + }, + toUnsignedIntegralNumeric: (n, $) => { + const prim = toPrimitive($); + if (typeof prim === "bigint") { + // The primitive value is a big·int. + return asUintN(n, prim); + } else { + // The primitive value is not a big·int. + const int = trunc(prim); + if (!isFiniteNumber(int) || int == 0) { + // The truncated value is zero or not finite. + return 0; + } else { + // The truncated value is finite. + return toNumber(asUintN(n, toBigInt(int))); + } + } + }, + }; +})();