UNPKG

@fleupold/dex-contracts

Version:

Contracts for dFusion multi-token batch auction exchange

136 lines (135 loc) 5.54 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Fraction = void 0; const bn_js_1 = __importDefault(require("bn.js")); const MAX_FLOAT_WIDTH = 1024; const MAX_FLOAT_PRECISION = 52; class Fraction { constructor(numerator, denominator) { this.numerator = new bn_js_1.default(numerator); this.denominator = new bn_js_1.default(denominator); this.reduce(); } isZero() { return this.numerator.isZero(); } eq(other) { const diff = this.sub(other); return diff.isZero(); } gt(other) { const diff = this.sub(other); return diff.numerator.mul(diff.denominator).gt(new bn_js_1.default(0)); } lt(other) { const diff = this.sub(other); return diff.numerator.mul(diff.denominator).lt(new bn_js_1.default(0)); } reduce() { const greatest_common_divisor = this.numerator.gcd(this.denominator); this.numerator = this.numerator.div(greatest_common_divisor); this.denominator = this.denominator.div(greatest_common_divisor); } inverted() { return new Fraction(this.denominator, this.numerator); } negated() { return new Fraction(this.numerator.neg(), this.denominator); } mul(other) { return new Fraction(this.numerator.mul(other.numerator), this.denominator.mul(other.denominator)); } abs() { return new Fraction(this.numerator.abs(), this.denominator.abs()); } div(other) { return this.mul(other.inverted()); } sub(other) { return this.add(other.negated()); } add(other) { return new Fraction(this.numerator .mul(other.denominator) .iadd(other.numerator.mul(this.denominator)), this.denominator.mul(other.denominator)); } toNumber() { let numerator = this.numerator.clone(); let denominator = this.denominator.clone(); // If ratio between numerator and denominator is larger than the maximum // float number precision, use integer division if (!denominator.isZero() && numerator.div(denominator).bitLength() > MAX_FLOAT_PRECISION) { numerator = numerator.div(denominator); denominator = new bn_js_1.default(1); } else if (!numerator.isZero() && denominator.div(numerator).bitLength() > MAX_FLOAT_PRECISION) { denominator = denominator.div(numerator); numerator = new bn_js_1.default(1); } // Prevent overflow by only keeping the most 1023 significant bits. // Since 2**1023 < Number.MAX_VALUE < 2**1024 this is safe. if (Math.max(parseInt(numerator.toString()), parseInt(denominator.toString())) === Infinity) { const longestWidth = Math.max(numerator.bitLength(), denominator.bitLength()); numerator = numerator.ishrn(longestWidth - MAX_FLOAT_WIDTH + 1); denominator = denominator.ishrn(longestWidth - MAX_FLOAT_WIDTH + 1); } return parseInt(numerator.toString()) / parseInt(denominator.toString()); } /** * Represents a Javascript number as a pair numerator/denominator without precision loss * by retrieving mantissa, exponent, and sign from its bit representation * @param number - The Javascript number to be represented * @returns a BigInt array with two elements: numerator and denominator */ static numberToNumAndDen(number) { const view = new DataView(new ArrayBuffer(8)); view.setFloat64(0, number); const bits = view.getBigUint64(0); const sign = bits >> BigInt(63) ? BigInt(-1) : BigInt(1); const exponent = ((bits >> BigInt(52)) & BigInt(0x7ff)) - BigInt(1023); const one = BigInt(1) << BigInt(52); const mantissa = bits & (one - BigInt(1)); // number is 1.mantissa * 2**exponent switch (exponent) { case BigInt(1024): // infinities and NaN throw Error("Invalid number"); case BigInt(-1023): if (mantissa == BigInt(0)) // positive and negative zero return [BigInt(0), sign]; // subnormal numbers else throw Error("Subnormal numbers are not supported"); } const mantissa_plus_one = mantissa + one; const shifted_exponent = exponent - BigInt(52); if (shifted_exponent >= BigInt(0)) return [ sign * mantissa_plus_one * (BigInt(1) << shifted_exponent), BigInt(1), ]; else return [sign * mantissa_plus_one, BigInt(1) << -shifted_exponent]; } static fromNumber(number) { const [numerator, denominator] = Fraction.numberToNumAndDen(number); return new Fraction(new bn_js_1.default(numerator.toString()), new bn_js_1.default(denominator.toString())); } toBN() { return this.numerator.div(this.denominator); } clone() { return new Fraction(this.numerator.clone(), this.denominator.clone()); } static fromJSON(o) { const numerator = new bn_js_1.default(o.numerator, "hex"); const denominator = new bn_js_1.default(o.denominator, "hex"); return new Fraction(numerator, denominator); } } exports.Fraction = Fraction;