//pin implementations const random = Math.random; const ceil = Math.ceil; const floor = Math.floor; /** * Rounds a number to one decimal places. * @param num The number to round. * @returns The input number rounded to one decimal places. */ export function roundTo1(num: number): number { return Math.round((num + Number.EPSILON) * 10) / 10; } /** * Rounds a number to two decimal places. * @param num The number to round. * @returns The input number rounded to two decimal places. */ export function roundTo2(num: number): number { return Math.round((num + Number.EPSILON) * 100) / 100; } /** * Calculates the mean (average) of an array of numbers. * @param array An array of numbers. * @returns The mean of the input array. */ export function mean(array: number[]): number { try { return ( array.reduce((previous, current) => (current += previous)) / array.length ); } catch (e) { return 0; } } /** * Calculates the standard deviation of an array of numbers. * @param array An array of numbers. * @returns The standard deviation of the input array. */ export function stdDev(array: number[]): number { try { const n = array.length; const meanValue = mean(array); return Math.sqrt( array.map((x) => Math.pow(x - meanValue, 2)).reduce((a, b) => a + b) / n, ); } catch (e) { return 0; } } /** * Calculates the median of an array of numbers. * https://www.w3resource.com/javascript-exercises/fundamental/javascript-fundamental-exercise-88.php * @param arr An array of numbers. * @returns The median of the input array. */ export function median(arr: number[]): number { try { const mid = Math.floor(arr.length / 2), nums = [...arr].sort((a, b) => a - b); return arr.length % 2 !== 0 ? (nums[mid] as number) : ((nums[mid - 1] as number) + (nums[mid] as number)) / 2; } catch (e) { return 0; } } /** * Calculates consistency by mapping COV from [0, +infinity) to [100, 0). * The mapping function is a version of the sigmoid function tanh(x) that is closer to the identity function tanh(arctanh(x)) in [0, 1). * @param cov The coefficient of variation of an array of numbers (standard deviation / mean). * @returns Consistency */ export function kogasa(cov: number): number { return ( 100 * (1 - Math.tanh(cov + Math.pow(cov, 3) / 3 + Math.pow(cov, 5) / 5)) ); } /** * Gets an integer between min and max, both are inclusive. * @param min * @param max * @returns Random integer betwen min and max. */ export function randomIntFromRange(min: number, max: number): number { const minNorm = ceil(min); const maxNorm = floor(max); return floor(random() * (maxNorm - minNorm + 1) + minNorm); } /** * Maps a value from one range to another. * @param value The value to map. * @param inMin Input range minimum. * @param inMax Input range maximum. * @param outMin Output range minimum. * @param outMax Output range maximum. * @param clamp If true, the result is clamped to the output range. Default true. * @returns The mapped value. */ export function mapRange( value: number, inMin: number, inMax: number, outMin: number, outMax: number, clamp = true, ): number { if (inMin === inMax) { return outMin; } const result = ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin; if (clamp) { if (outMin < outMax) { return Math.min(Math.max(result, outMin), outMax); } else { return Math.max(Math.min(result, outMin), outMax); } } return result; } /** * Checks if a value is a safe number. Safe numbers are finite and not NaN. * @param value The value to check. * @returns True if the value is a safe number, false otherwise. */ export function isSafeNumber(value: unknown): value is number { if (typeof value === "number") { return !isNaN(value) && isFinite(value); } return false; } /** * Converts a number to a safe number or undefined. NaN, Infinity, and -Infinity are converted to undefined. * @param value The value to convert. * @returns The input number if it is safe, undefined otherwise. */ export function safeNumber( value: number | undefined | null, ): number | undefined { if (isSafeNumber(value)) { return value; } return undefined; }