UNPKG

typia

Version:

Superfast runtime validators with only one line

168 lines • 7.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports._randomMultiple = void 0; const _decimal_1 = require("./_decimal"); const _isMultipleOf_1 = require("./_isMultipleOf"); const _randomMultiple = (props) => { const step = props.integer ? (0, _decimal_1._decimalIntegerStep)(props.multipleOf) : (0, _decimal_1._decimalDecompose)(props.multipleOf); if (step === null || step.coefficient <= BigInt(0)) throw new Error("The multipleOf value must be a positive finite number."); const lower = (0, _decimal_1._decimalDivide)(props.minimum, step); const upper = (0, _decimal_1._decimalDivide)(props.maximum, step); if (lower === null || upper === null) throw new Error("The random number range must be finite."); const minimum = lowerBound(lower, props.exclusiveMinimum); const maximum = upperBound(upper, props.exclusiveMaximum); if (minimum > maximum) throw new Error("The range does not contain a multipleOf value."); const selected = randomBigint(minimum, maximum); const candidates = unique([ selected, minimum, maximum, clamp(BigInt(0), minimum, maximum), clamp(BigInt(1), minimum, maximum), clamp(BigInt(-1), minimum, maximum), ...nearby(selected, minimum, maximum), ]); for (const coefficient of candidates) { const value = (0, _decimal_1._decimalToNumber)({ coefficient: step.coefficient * coefficient, exponent: step.exponent, }); if (isValid(props, value)) return value; } const aligned = findRepresentableIntegerMultiple(props); if (aligned !== null) return aligned; const decimalAligned = findRepresentableDecimalMultiple(props, step); if (decimalAligned !== null) return decimalAligned; throw new Error("The range does not contain a representable multipleOf value."); }; exports._randomMultiple = _randomMultiple; const isValid = (props, value) => Number.isFinite(value) && (props.integer === false || Number.isInteger(value)) && (props.exclusiveMinimum ? value > props.minimum : value >= props.minimum) && (props.exclusiveMaximum ? value < props.maximum : value <= props.maximum) && (0, _isMultipleOf_1._isMultipleOf)(value, props.multipleOf); const findRepresentableDecimalMultiple = (props, step) => { const limit = BigInt("999999999999999"); for (let exponent = -324; exponent <= 308; ++exponent) { const unit = { coefficient: BigInt(1), exponent }; const lower = (0, _decimal_1._decimalDivide)(props.minimum, unit); const upper = (0, _decimal_1._decimalDivide)(props.maximum, unit); if (lower === null || upper === null) return null; const coefficientMinimum = max(-limit, lowerBound(lower, props.exclusiveMinimum)); const coefficientMaximum = min(limit, upperBound(upper, props.exclusiveMaximum)); if (coefficientMinimum > coefficientMaximum) continue; const coefficientStep = decimalCoefficientStep(step, exponent); const minimum = lowerBound({ numerator: coefficientMinimum, denominator: coefficientStep }, false); const maximum = upperBound({ numerator: coefficientMaximum, denominator: coefficientStep }, false); if (minimum > maximum) continue; const selected = randomBigint(minimum, maximum); for (const quotient of unique([ selected, minimum, maximum, clamp(BigInt(0), minimum, maximum), ...nearby(selected, minimum, maximum), ])) { const value = (0, _decimal_1._decimalToNumber)({ coefficient: coefficientStep * quotient, exponent, }); if (isValid(props, value)) return value; } } return null; }; const decimalCoefficientStep = (step, exponent) => { const difference = exponent - step.exponent; if (difference >= 0) { const power = (0, _decimal_1._decimalPower)(difference); return step.coefficient / (0, _decimal_1._decimalGcd)(step.coefficient, power); } return step.coefficient * (0, _decimal_1._decimalPower)(-difference); }; const findRepresentableIntegerMultiple = (props) => { const step = (0, _decimal_1._decimalIntegerStep)(props.multipleOf); if (step === null) return null; const unit = { coefficient: BigInt(1), exponent: 0 }; const lower = (0, _decimal_1._decimalDivide)(props.minimum, unit); const upper = (0, _decimal_1._decimalDivide)(props.maximum, unit); if (lower === null || upper === null) return null; const minimum = lowerBound(lower, props.exclusiveMinimum); const maximum = upperBound(upper, props.exclusiveMaximum); if (minimum > maximum) return null; if (minimum <= BigInt(0) && maximum >= BigInt(0)) return 0; const candidate = minimum > BigInt(0) ? findPositiveAligned(minimum, maximum, step.coefficient) : (() => { const magnitude = findPositiveAligned(-maximum, -minimum, step.coefficient); return magnitude === null ? null : -magnitude; })(); if (candidate === null) return null; const value = Number(candidate); return isValid(props, value) ? value : null; }; const findPositiveAligned = (minimum, maximum, integerStep) => { const first = bitLength(minimum) - 1; const last = bitLength(maximum) - 1; for (let exponent = first; exponent <= last; ++exponent) { const bandMinimum = max(minimum, BigInt(1) << BigInt(exponent)); const bandMaximum = min(maximum, (BigInt(1) << BigInt(exponent + 1)) - BigInt(1)); const quantum = exponent <= 52 ? BigInt(1) : BigInt(1) << BigInt(exponent - 52); const alignedStep = (integerStep / (0, _decimal_1._decimalGcd)(integerStep, quantum)) * quantum; const lower = lowerBound({ numerator: bandMinimum, denominator: alignedStep }, false); const upper = upperBound({ numerator: bandMaximum, denominator: alignedStep }, false); if (lower <= upper) return randomBigint(lower, upper) * alignedStep; } return null; }; const bitLength = (value) => value.toString(2).length; const min = (x, y) => (x < y ? x : y); const max = (x, y) => (x > y ? x : y); const lowerBound = (ratio, exclusive) => { const quotient = ratio.numerator / ratio.denominator; const remainder = ratio.numerator % ratio.denominator; const ceiling = quotient + (remainder > BigInt(0) ? BigInt(1) : BigInt(0)); return (ceiling + (exclusive && remainder === BigInt(0) ? BigInt(1) : BigInt(0))); }; const upperBound = (ratio, exclusive) => { const quotient = ratio.numerator / ratio.denominator; const remainder = ratio.numerator % ratio.denominator; const floor = quotient - (remainder < BigInt(0) ? BigInt(1) : BigInt(0)); return floor - (exclusive && remainder === BigInt(0) ? BigInt(1) : BigInt(0)); }; const randomBigint = (minimum, maximum) => { const scale = BigInt(1) << BigInt(53); const sample = BigInt(Math.min(Number(scale - BigInt(1)), Math.floor(Math.max(0, Math.random()) * Number(scale)))); return minimum + ((maximum - minimum + BigInt(1)) * sample) / scale; }; const clamp = (value, minimum, maximum) => value < minimum ? minimum : value > maximum ? maximum : value; const nearby = (selected, minimum, maximum) => { const output = []; for (let distance = BigInt(1); distance <= BigInt(32); ++distance) { if (selected - distance >= minimum) output.push(selected - distance); if (selected + distance <= maximum) output.push(selected + distance); } return output; }; const unique = (values) => [...new Set(values)]; //# sourceMappingURL=_randomMultiple.js.map