UNPKG

meows

Version:
190 lines (188 loc) 6.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const functions_1 = require("./functions"); const types_1 = require("./types"); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Numerics and Random /** * A suite of random tools. */ class Rand { /** * Given natural `n` → returns `n` strong random booleans. * @example * Rand.bool() ? 'heads' : 'tails' * Rand.bool(10).filter(x => x) */ static bool(nat = 1) { if (!Number.isSafeInteger(nat) || nat < 1) throw new TypeError(`Rand::bool() requires natural input larger than 0, but got: ${nat}.`); let booleans = new Uint8Array(nat); crypto.getRandomValues(booleans); return nat === 1 ? 127 < booleans[0] : Array.from(booleans).map(n => 127 < nat); } } Rand.int = (min = -255, max = 255) => Math.trunc(Math.random() * (max + -min + 1)) + min; Rand.real = (min = -255, max = 255) => Math.random() * (max - min) + min; /** Gives a weak random boolean. */ Rand.wBool = () => Math.random() >= 0.5; exports.Rand = Rand; /** Generate an array of `n` strong random integers. */ exports.randomInts = (n = 1) => window.crypto.getRandomValues(new Int16Array(n)); exports.sum = (nums) => Array.isArray(nums) ? nums.reduce((state, n) => state + n) : NaN; exports.product = (nums) => Array.isArray(nums) ? nums.reduce((state, n) => state * n) : NaN; /** * Finds the difference in magnitude between two numbers. * @example * diff_abs(10, -3) // => 13 * diff_abs(-5, 5) // => 10 */ exports.diff_abs = (a, b) => Math.abs(a - b); /** * Finds the delta of unidimensional array of reals. * @example * diff_nums([0, 0, 1, 3, 6, 10, 15]) // => [0, 1, 2, 3, 4, 5] */ exports.diff_nums = (nums) => Array.isArray(nums) && Boolean(nums.length) ? functions_1.zipWith((a, b) => b - a, nums.slice(0, nums.length - 1), nums.slice(1)) : []; /** * Converts an integer to an array of its digits. * @example * int_to_digits(123450) // => [1, 2, 3, 4, 5, 0] * int_to_digits(1/0) // => [] */ exports.int_to_digits = (int, base = 10) => Number.isSafeInteger(int) ? int.toString(base).split('').map(digit => parseInt(digit, base)) : []; /** * Randomly signs a signable number. If the value isn't signable, return `NaN`. * @example * const backoff = (tries) => 2**tries + plus_or_minus(Math.random()) */ exports.plus_or_minus = (num) => typeof num === 'number' && !Number.isNaN(num) ? Math.random() >= 0.5 ? -num : +num : NaN; /** Check if strictly equal to `0`. */ exports.isZero = num => num === 0; /** Floor division that returns `NaN` for non-number inputs. */ exports.fdiv = (num, div) => functions_1.always(types_1.isType.num, [num, div]) ? Math.trunc(num / div) : NaN; /** * Returns the result of a division operation in the form of an array, with the * first item as the whole quotient, and the second item as the remainder. * * @example * divmod(13, 2) // => [6, 1] * divmod(12, 2) // => [6, 0] * divmod(12.5, 2) // => [6, 0.5] */ exports.divmod = (num, div) => functions_1.always(types_1.isType.num, [num, div]) ? [Math.trunc(num / div), num % div] : [NaN, NaN]; /** * Takes a decimal number and separates its whole part from its fractional part. * * @example * splitDecimal(3) // => [3, 0] * splitDecimal(3.5) // => [3, 0.5] * splitDecimal(0.5) // => [0, 0.5] * splitDecimal(0) // => [0, 0] * splitDecimal(null) // => [NaN, NaN] */ function splitDecimal(real) { const raw = String(real).split('.'); const [whole, part] = [parseInt(raw[0]), parseFloat(`0.` + raw[1])]; return types_1.isType.int(whole) && types_1.isType.num(part) ? [whole, part] : [NaN, NaN]; } exports.splitDecimal = splitDecimal; /** Replacement for `Math.floor`. */ exports.floor = Math.trunc; /** Changes a signable number into negative. */ exports.negative = (num) => types_1.isType.signable(num) ? -Math.abs(num) : NaN; /** Changes a signable number into positive. */ exports.positive = (num) => types_1.isType.signable(num) ? +Math.abs(num) : NaN; /** * Flips the sign on a signable number, but excludes `null`. * @example * flipSign(0) // => -0 * flipSign(-0) // => 0 * flipSign(1/0) // => -Infinity * flipSign(NaN) // => NaN * flipSign(null) // => NaN */ exports.flipSign = (num) => types_1.isType.signable(num) ? -num : NaN; /** * Takes target real number and a maximum denominator for precision and finds * the simplest and closest fraction up to that target real in the form of an * array: `[numerator, divisor]`. * @example * realToFraction(0.23, 10) // => [2, 9] * realToFraction(0.31415, 100) // => [11, 35] * realToFraction(NaN, NaN) // => [NaN, NaN] */ function realToFraction(real, denMax) { if (!types_1.isType.num(real) || !types_1.isType.nat(denMax)) throw new TypeError(`realFraction() requires a target real number and a natural number max denominator, but got:` + `\n arg[0]: ${types_1.typeName(real)}` + `\n arg[1]: ${types_1.typeName(denMax)}`); let num_a = 0; let den_a = 1; let num_b = 1; let den_b = 1; const getMediant = () => (num_a + num_b) / (den_a + den_b); const isUnderLimit = () => den_a <= denMax && den_b <= denMax; const sumNum = () => num_a + num_b; const sumDen = () => den_a + den_b; while (isUnderLimit()) { if (real === getMediant()) { if (sumDen() <= denMax) return [sumNum(), sumDen()]; if (den_a < den_b) return [num_b, den_b]; else return [num_a, den_a]; } if (getMediant() < real) { num_a = sumNum(); den_a = sumDen(); } else { num_b = sumNum(); den_b = sumDen(); } } return den_a > denMax ? [num_b, den_b] : [num_a, den_a]; } exports.realToFraction = realToFraction; /** * Simplifies a fraction by recursively looking for greatest common divisor * ("gcd") of the numerator and divisor (denominator). Returns a fraction in the * form of an array, with the numerator being the first item, and the divisor * being the second. * * @example * simplifyFraction(6/3) // => [2, 1] * simplifyFraction(3/6) // => [1, 2] * simplifyFraction(1/2) // => [1, 2] * simplifyFraction(1/0) // => [NaN, NaN] */ function simplifyFraction(num, div) { let gcd = (a, b) => b ? gcd(b, a % b) : a; let result = gcd(num, div); return [num, div].every(types_1.isType.num) && div !== 0 ? [num / result, div / result] : [NaN, NaN]; } exports.simplifyFraction = simplifyFraction;