UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

123 lines (122 loc) 4.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.assertStatisticsArray = assertStatisticsArray; exports.toNumericSequence = toNumericSequence; exports.toStatisticNumber = toStatisticNumber; exports.sortStatisticsValues = sortStatisticsValues; exports.statisticsMeanFromSequence = statisticsMeanFromSequence; exports.statisticsVarianceFromSequence = statisticsVarianceFromSequence; exports.sumNumbers = sumNumbers; exports.sumProducts = sumProducts; exports.isIntegralStatisticValue = isIntegralStatisticValue; exports.toFloatStatisticNumber = toFloatStatisticNumber; function assertStatisticsArray(data, functionName) { if (!Array.isArray(data)) { throw new TypeError(`${functionName}() data must be an array`); } return data; } function toNumericSequence(data, functionName) { const values = assertStatisticsArray(data, functionName); return { values: values.map((value) => toStatisticNumber(value, functionName)), integral: values.every(isIntegralStatisticValue), }; } function toStatisticNumber(value, functionName) { if (typeof value === 'number') { return value; } if (typeof value === 'boolean') { return value ? 1 : 0; } if (typeof value === 'bigint') { const numericValue = Number(value); if (!Number.isSafeInteger(numericValue)) { throw new RangeError(`${functionName}() bigint values must fit within JS safe integers`); } return numericValue; } throw new TypeError(`${functionName}() data must contain only real numbers`); } function sortStatisticsValues(data, functionName) { const values = assertStatisticsArray(data, functionName); if (values.length === 0) { return []; } const firstKind = getStatisticsSortKind(values[0], functionName); for (let index = 1; index < values.length; index += 1) { const currentKind = getStatisticsSortKind(values[index], functionName); if (currentKind !== firstKind) { throw new TypeError(`${functionName}() requires data values that can be ordered together`); } } return [...values].sort((left, right) => compareStatisticsValues(left, right, firstKind)); } function statisticsMeanFromSequence(sequence, preferFloat = false) { return divideStatisticsValue(sumNumbers(sequence.values), sequence.values.length, sequence.integral, preferFloat); } function statisticsVarianceFromSequence(sequence, divisor, functionName, center) { const mean = center === undefined ? statisticsMeanFromSequence(sequence) : toStatisticNumber(center, functionName); const squaredDeltaSum = sequence.values.reduce((sum, value) => { const delta = value - mean; return sum + delta * delta; }, 0); return divideStatisticsValue(squaredDeltaSum, divisor, sequence.integral); } function sumNumbers(values) { return values.reduce((sum, value) => sum + value, 0); } function sumProducts(left, right) { let total = 0; for (let index = 0; index < left.length; index += 1) { total += (left[index] ?? 0) * (right[index] ?? 0); } return total; } function isIntegralStatisticValue(value) { return (typeof value === 'boolean' || typeof value === 'bigint' || (typeof value === 'number' && Number.isInteger(value))); } function toFloatStatisticNumber(value, functionName) { if (typeof value === 'number') { return value; } if (typeof value === 'boolean') { return value ? 1 : 0; } if (typeof value === 'bigint') { return Number(value); } if (typeof value === 'string') { const parsed = Number(value); if (!Number.isNaN(parsed)) { return parsed; } throw new TypeError('Value cannot be converted to a float'); } throw new TypeError(`${functionName}() data must contain only real numbers`); } function divideStatisticsValue(total, divisor, integral, preferFloat = false) { if (!preferFloat && integral && Number.isSafeInteger(total) && total % divisor === 0) { return total / divisor; } return total / divisor; } function getStatisticsSortKind(value, functionName) { if (typeof value === 'string') { return 'string'; } if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') { return 'number'; } throw new TypeError(`${functionName}() requires sortable scalar values`); } function compareStatisticsValues(left, right, kind) { if (kind === 'string') { return String(left).localeCompare(String(right)); } return toNumericSortValue(left) - toNumericSortValue(right); } function toNumericSortValue(value) { return typeof value === 'boolean' ? (value ? 1 : 0) : Number(value); }