typia
Version:
Superfast runtime validators with only one line
168 lines • 7.89 kB
JavaScript
"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