tough-rational
Version:
Rational class using BigInt with fallback to bignumber.js
88 lines (78 loc) • 2.91 kB
JavaScript
;
const generate = require('./generate');
const gcd = require('./gcd');
const util = require('../util');
const compat = require('../compat');
const pow = require('./pow-generator')();
const bufferToHex = require('../util/buffer-to-hex');
const isBuffer = require('../util/is-buffer');
const flow = require('../util/flow');
const parseFromBigInt = n => [n, BigInt(1)];
let parseFromCoercedBigInt;
let mapToBigInt;
if (compat.testForBigInt()) {
parseFromCoercedBigInt = util.flow(BigInt, parseFromBigInt);
mapToBigInt = util.map(BigInt);
}
const parseFromNumber = n => {
if (n !== n) return generate.generateNaN();
if (n === Infinity) return generate.generateInfinity();
if (n === -Infinity) return generate.generateNegativeInfinity();
const remainder = n % 1;
if (!remainder) return parseFromCoercedBigInt(n);
return parseFromString(String(n));
};
const exponentialRe = /^([0-9]+(?:\.[0-9]+)?)e([0-9]+)/;
const numberRe = /(?:[0-9]*(?:\.[0-9]*)?|0x[0-9a-zA-Z]*)/;
const beginsWithHexPrefixRe = /^0x/;
const parseFromString = s => {
let match;
if ((match = s.match(exponentialRe))) {
const decimalAt = match[1].lastIndexOf('.');
const decimalPlaces = ~decimalAt ? match[1].length - decimalAt - 1 : 0;
return gcd.gcdReduce([
pow(BigInt(10), BigInt(+match[2])) * BigInt(match[1].replace('.', '')),
pow(BigInt(10), BigInt(decimalPlaces)),
]);
}
if (!s.match(numberRe)) return generateNaN();
if (s.match(beginsWithHexPrefixRe)) return parseFromHexString(s);
if (util.last(s) === '.') s.replace('.', '');
const lastDecimal = s.lastIndexOf('.');
if (!~lastDecimal) return parseFromCoercedBigInt(s);
const decimalPlaces = s.length - lastDecimal - 1;
return gcd.gcdReduce([
BigInt(s.replace('.', '')),
pow(BigInt(10), BigInt(decimalPlaces)),
]);
};
const parseFromHexString = parseFromCoercedBigInt;
const parseFromBuffer = flow(bufferToHex, parseFromHexString);
const parseDynamic = input =>
isBuffer(input)
? parseFromBuffer(input)
: typeof input === 'string'
? parseFromString(input)
: typeof input === 'number'
? parseFromNumber(input)
: typeof input === 'bigint'
? parseFromBigInt(input)
: typeof input === 'boolean'
? input ? parseFromBigInt(BigInt(1)) : generate.generateZero()
: typeof input === 'object' && Array.isArray(input)
? mapToBigInt(input)
: typeof input === 'object' &&
(typeof input.numerator === 'bigint' ||
typeof input.numerator === 'symbol') &&
typeof input.denominator === 'bigint'
? input._getPrimitives()
: generate.generateZero();
Object.assign(module.exports, {
parseFromBigInt,
parseFromCoercedBigInt,
parseFromNumber,
parseFromString,
parseFromBuffer,
parseFromHexString,
parseDynamic,
});