🏡 index : cold-air-networks/mirror-jetstream.git

// MIT License

// Copyright (c) 2020 U-Zyn Chua <chua@uzyn.com>

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

globalThis.BigDenary = (function () {
    'use strict';

    function bigIntAbs(n) {
        if (n >= 0n) {
            return n;
        }
        return n * -1n;
    }
    function getDecimals(n) {
        if (isNaN(n)) {
            throw new Error("InvalidNumber");
        }
        const [preDec, postDec] = _splitString(n.toString(), ".");
        return postDec.length;
    }
    function extractExp(n) {
        const [mul, expStr] = _splitString(n, "e");
        if (expStr === "") {
            return [n, 0];
        }
        const exp = parseInt(expStr, 10);
        if (isNaN(exp)) {
            throw new Error("InvalidNumber");
        }
        return [mul, exp];
    }
    function countTrailingZeros(n, upTo) {
        if (n === 0n) {
            return 0;
        }
        let count = 0;
        let c = n < 0 ? n * -1n : n;
        while (c % 10n === 0n && count < upTo) {
            count += 1;
            c = c / 10n;
        }
        return count;
    }
    function _splitString(input, char) {
        const pos = input.indexOf(char);
        if (pos === -1) {
            return [input, ""];
        }
        const after = input.substr(pos + 1);
        if (after.indexOf(char) !== -1) {
            throw new Error("InvalidNumber"); // Multiple occurences
        }
        return [input.substr(0, pos), after];
    }

    const BDCompare = {
        Greater: 1,
        Less: -1,
        Equal: 0,
    };

    class BigDenary {
        constructor(n) {
            if (n instanceof BigDenary) {
                this.base = n.base;
                this._decimals = n.decimals;
            }
            else if (typeof n === "number") {
                this._decimals = getDecimals(n);
                this.base = BigInt(n * Math.pow(10, this._decimals));
            }
            else if (typeof n === "string") {
                const [mul, exp] = extractExp(n);
                const mulDec = getDecimals(mul);
                if (exp > mulDec) {
                    this.base = BigInt(mul.replace(".", "")) *
                        BigInt(Math.pow(10, (exp - mulDec)));
                    this._decimals = 0;
                }
                else {
                    this.base = BigInt(mul.replace(".", ""));
                    this._decimals = mulDec - exp;
                }
            }
            else if (typeof n === "bigint") {
                this.base = n * this.decimalMultiplier;
                this._decimals = 0;
            }
            else if (typeof n === "object" && n !== null) {
                if (n.decimals < 0) {
                    throw new Error("InvalidBigDenaryRaw");
                }
                this.base = n.base;
                this._decimals = n.decimals;
            }
            else {
                throw new Error("UnsupportedInput");
            }
            this.trimTrailingZeros();
        }
        toString() {
            if (this.base === 0n) {
                return "0";
            }
            const negative = (this.base < 0);
            let base = this.base;
            if (negative) {
                base = base * -1n;
            }
            const baseStr = base.toString();
            const position = baseStr.length - this._decimals;
            let pre;
            let post;
            if (position < 0) {
                pre = "";
                post = `${_strOfZeros(position * -1)}${baseStr}`;
            }
            else {
                pre = baseStr.substr(0, position);
                post = baseStr.substr(position);
            }
            let result;
            if (pre.length === 0) {
                result = `0.${post}`;
            }
            else if (post.length === 0) {
                result = `${pre}`;
            }
            else {
                result = `${pre}.${post}`;
            }
            if (negative) {
                return `-${result}`;
            }
            return result;
        }
        valueOf() {
            return Number.parseFloat(this.toString());
        }
        toFixed(digits) {
            if (!digits) {
                return this.toString();
            }
            const temp = new BigDenary(this);
            temp.scaleDecimalsTo(digits);
            return temp.toString();
        }
        get decimals() {
            return this._decimals;
        }
        /**
         * Alters the decimal places, actual underlying value does not change
         */
        scaleDecimalsTo(_decimals) {
            if (_decimals > this._decimals) {
                this.base = this.base *
                    BigDenary.getDecimalMultiplier(_decimals - this._decimals);
            }
            else if (_decimals < this._decimals) {
                const adjust = this._decimals - _decimals;
                const multiplier = BigDenary.getDecimalMultiplier(adjust);
                const remainder = this.base % multiplier;
                this.base = this.base / multiplier;
                if (bigIntAbs(remainder * 2n) >= multiplier) {
                    if (this.base >= 0) {
                        this.base += 1n;
                    }
                    else {
                        this.base -= 1n;
                    }
                }
            }
            this._decimals = _decimals;
        }
        get decimalMultiplier() {
            return BigDenary.getDecimalMultiplier(this._decimals);
        }
        static getDecimalMultiplier(decimals) {
            return 10n ** BigInt(decimals);
        }
        trimTrailingZeros() {
            const trailingZerosCount = countTrailingZeros(this.base, this.decimals);
            if (trailingZerosCount > 0) {
                this.scaleDecimalsTo(this.decimals - trailingZerosCount);
            }
        }
        /**
         * Operations
         */
        plus(operand) {
            const curr = new BigDenary(this);
            const oper = new BigDenary(operand);
            const targetDecs = Math.max(curr.decimals, oper.decimals);
            curr.scaleDecimalsTo(targetDecs);
            oper.scaleDecimalsTo(targetDecs);
            return new BigDenary({
                base: curr.base + oper.base,
                decimals: targetDecs,
            });
        }
        minus(operand) {
            return this.plus((new BigDenary(operand)).negated());
        }
        multipliedBy(operand) {
            const curr = new BigDenary(this);
            const oper = new BigDenary(operand);
            const targetDecs = curr.decimals + oper.decimals;
            return new BigDenary({
                base: curr.base * oper.base,
                decimals: targetDecs,
            });
        }
        dividedBy(operand) {
            const MIN_DIVIDE_DECIMALS = 20;
            const curr = new BigDenary(this);
            const oper = new BigDenary(operand);
            const targetDecs = Math.max(curr.decimals * 2, oper.decimals * 2, MIN_DIVIDE_DECIMALS);
            curr.scaleDecimalsTo(targetDecs);
            return new BigDenary({
                base: curr.base / oper.base,
                decimals: curr.decimals - oper.decimals,
            });
        }
        negated() {
            return new BigDenary({
                base: this.base * -1n,
                decimals: this.decimals,
            });
        }
        absoluteValue() {
            if (this.base >= 0n) {
                return this;
            }
            return this.negated();
        }
        /**
         * Comparisons
         */
        comparedTo(comparator) {
            const curr = new BigDenary(this);
            const comp = new BigDenary(comparator);
            const targetDecs = Math.max(curr.decimals, comp.decimals);
            curr.scaleDecimalsTo(targetDecs);
            comp.scaleDecimalsTo(targetDecs);
            if (curr.base > comp.base) {
                return BDCompare.Greater;
            }
            else if (curr.base < comp.base) {
                return BDCompare.Less;
            }
            return BDCompare.Equal;
        }
        equals(comparator) {
            return (this.comparedTo(comparator) === BDCompare.Equal);
        }
        greaterThan(comparator) {
            return (this.comparedTo(comparator) === BDCompare.Greater);
        }
        greaterThanOrEqualTo(comparator) {
            return ((this.comparedTo(comparator) === BDCompare.Greater) ||
                (this.comparedTo(comparator) === BDCompare.Equal));
        }
        lessThan(comparator) {
            return (this.comparedTo(comparator) === BDCompare.Less);
        }
        lessThanOrEqualTo(comparator) {
            return ((this.comparedTo(comparator) === BDCompare.Less) ||
                (this.comparedTo(comparator) === BDCompare.Equal));
        }
        /**
         * Shortforms
         */
        add(operand) {
            return this.plus(operand);
        }
        sub(operand) {
            return this.minus(operand);
        }
        mul(operand) {
            return this.multipliedBy(operand);
        }
        div(operand) {
            return this.dividedBy(operand);
        }
        neg() {
            return this.negated();
        }
        abs() {
            return this.absoluteValue();
        }
        cmp(comparator) {
            return this.comparedTo(comparator);
        }
        eq(comparator) {
            return this.equals(comparator);
        }
        gt(comparator) {
            return this.greaterThan(comparator);
        }
        gte(comparator) {
            return this.greaterThanOrEqualTo(comparator);
        }
        lt(comparator) {
            return this.lessThan(comparator);
        }
        lte(comparator) {
            return this.lessThanOrEqualTo(comparator);
        }
    }
    function _strOfZeros(count) {
        return "0".repeat(count);
    }

    BigDenary.BDCompare = BDCompare;

    return BigDenary;

})();