meows
Version:
A kittybin of tools.
190 lines (188 loc) • 6.92 kB
JavaScript
;
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;