var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _BigInteger_value;
import { reverseHex } from "./basic";
import BN from "bn.js";
import { HexString } from "./HexString";
/**
 * This is a BigInteger representation, providing several helper methods to convert between different formats as well as basic math operations.
 * Mainly used within the NEO VM for representing numbers.
 */
export class BigInteger {
    constructor(value) {
        /** Internal holding value. */
        _BigInteger_value.set(this, void 0);
        __classPrivateFieldSet(this, _BigInteger_value, value, "f");
    }
    static fromTwos(hexstring, littleEndian = false) {
        const cleanedInput = hexstring instanceof HexString
            ? hexstring
            : HexString.fromHex(hexstring, littleEndian);
        return new BigInteger(new BN(cleanedInput.toBigEndian(), 16).fromTwos(cleanedInput.byteLength * 8));
    }
    static fromHex(hexstring, littleEndian = false) {
        const cleanedInput = hexstring instanceof HexString
            ? hexstring
            : HexString.fromHex(hexstring, littleEndian);
        return new BigInteger(new BN(cleanedInput.toBigEndian(), 16));
    }
    /**
     * Creates a BigInteger from a number.
     * @param input - Javascript number or string containing numbers.
     *
     * @throws non-integer provided
     * @throws Input is not a stringified number.
     *
     * @example
     * const zero = BigInteger.fromNumber(0);
     * const negativeOne = BigInteger.fromNumber(-1);
     *
     * const usingString = BigInteger.fromNumber("-1");
     */
    static fromNumber(input) {
        switch (typeof input) {
            case "string":
                if (input.indexOf(".") !== -1) {
                    throw new Error(`BigInteger only accepts integers. Got ${input}`);
                }
                return new BigInteger(new BN(input));
            case "number":
                if (input % 1 !== 0) {
                    throw new Error(`BigInteger only accepts integers. Got ${input}`);
                }
                // We use hex notation here as toString occasionally outputs scientific notation.
                // eg. 9999999999999999999999999999999999.toString() == '1e+34'.
                return new BigInteger(new BN(input.toString(16), 16));
            default:
                throw new Error(`Input was not stringified number or number: ${typeof input} ${input}`);
        }
    }
    /**
     * Creates a BigInteger instance from a decimal by parsing the decimals and shifting the decimal point by a provided number of places.
     *
     * This is mainly used with dealing with Nep17 tokens.
     * While most tokens support some sort of decimal places, the data is actually stored as an integer.
     * This helper method converts the decimal number to the integer representation to work with.
     * To convert back, use toDecimal(decimals);
     * @param input - Javascript number or string containing numbers. Accepts decimals.
     * @param decimals - Number of decimal places to support.
     *
     * @example
     *
     * const transformedDecimal = BigInteger.fromDecimal(12.34,3);
     * console.log(transformedDecimal.toString()); // 12340
     *
     * const oneGas = BigInteger.fromDecimal(1,8);
     * console.log(oneGas); // 100000000
     */
    static fromDecimal(input, decimals) {
        const stringNumber = typeof input === "number" ? input.toFixed(decimals) : input;
        const portions = stringNumber.split(".", 2);
        const leftOfDecimal = portions[0];
        const rightOfDecimal = portions.length === 2 ? portions[1] : "";
        // Throw if the right side is too long as it affects how we form the final number.
        if (rightOfDecimal.length > decimals) {
            throw new Error(`Input had more decimal places than provided. Got ${rightOfDecimal} but only got ${decimals} decimal places.`);
        }
        const finalNumber = leftOfDecimal +
            rightOfDecimal +
            "0".repeat(decimals - rightOfDecimal.length);
        return BigInteger.fromNumber(finalNumber);
    }
    /**
     * Returns a big endian hex representation of the integer.
     * Does not come with a prefix.
     */
    toHex() {
        const result = __classPrivateFieldGet(this, _BigInteger_value, "f").toString(16);
        return result.length % 2 !== 0 ? "0" + result : result;
    }
    /**
     * Returns a little endian hex representation of the integer.
     * Does not come with a prefix.
     */
    toReverseHex() {
        return reverseHex(this.toHex());
    }
    /**
     * Returns a big endian two's complement representation of the integer.
     * Does not come with a prefix.
     */
    toTwos() {
        const byteLength = getBytesForTwos(__classPrivateFieldGet(this, _BigInteger_value, "f"));
        const result = __classPrivateFieldGet(this, _BigInteger_value, "f")
            .toTwos(byteLength * 8)
            .toString(16, byteLength * 2);
        return result.length % 2 !== 0 ? "0" + result : result;
    }
    /**
     * Returns a little endian two's complement representation of the integer.
     * Does not come with a prefix.
     */
    toReverseTwos() {
        return reverseHex(this.toTwos());
    }
    /**
     * Returns the arabic numerical representation of the integer as a string.
     */
    toString() {
        return __classPrivateFieldGet(this, _BigInteger_value, "f").toString();
    }
    // We do not provide a toNumber() as it is unsafe conversion.
    /**
     * Converts the BigInteger into a decimal number by shifting the decimal place to the left.
     * @param decimals - Number of decimals places
     *
     * @example
     * const bigNumber = BigInteger.fromNumber(100000000);
     * console.log(bigNumber.toDecimal(8)); // 1.00000000
     */
    toDecimal(decimals) {
        if (decimals === 0) {
            return __classPrivateFieldGet(this, _BigInteger_value, "f").toString();
        }
        const sign = __classPrivateFieldGet(this, _BigInteger_value, "f").isNeg() ? "-" : "";
        const stringNumber = __classPrivateFieldGet(this, _BigInteger_value, "f").abs().toString(10);
        if (stringNumber.length <= decimals) {
            return sign + "0." + stringNumber.padStart(decimals, "0");
        }
        const leftOfDecimal = stringNumber.slice(0, stringNumber.length - decimals);
        const rightOfDecimal = stringNumber.slice(leftOfDecimal.length);
        return sign + leftOfDecimal + "." + rightOfDecimal;
    }
    /**
     * Adds the other value to this value and returns the result as a new BigInteger.
     * @param other - other operand to add.
     */
    add(other) {
        const otherBigInteger = other instanceof BigInteger ? other : BigInteger.fromNumber(other);
        return new BigInteger(__classPrivateFieldGet(this, _BigInteger_value, "f").add(__classPrivateFieldGet(otherBigInteger, _BigInteger_value, "f")));
    }
    /**
     * Subtracts the other value from this value and returns the result as a new BigInteger.
     * @param other - other operand to subtract.
     */
    sub(other) {
        const otherBigInteger = other instanceof BigInteger ? other : BigInteger.fromNumber(other);
        return new BigInteger(__classPrivateFieldGet(this, _BigInteger_value, "f").sub(__classPrivateFieldGet(otherBigInteger, _BigInteger_value, "f")));
    }
    /**
     * Multiply the other value with this value and returns the result as a new BigInteger.
     * @param other - other operand to multiply.
     */
    mul(other) {
        const otherBigInteger = other instanceof BigInteger ? other : BigInteger.fromNumber(other);
        return new BigInteger(__classPrivateFieldGet(this, _BigInteger_value, "f").mul(__classPrivateFieldGet(otherBigInteger, _BigInteger_value, "f")));
    }
    /**
     * Divides this value with the other value and returns the result as a new BigInteger.
     * There are no decimals and results are always rounded down.
     * @param other - other operand to divide.
     */
    div(other) {
        const otherBigInteger = other instanceof BigInteger ? other : BigInteger.fromNumber(other);
        return new BigInteger(__classPrivateFieldGet(this, _BigInteger_value, "f").div(__classPrivateFieldGet(otherBigInteger, _BigInteger_value, "f")));
    }
    /**
     * Performs the MOD operation with the other value and returns the result as a new BigInteger.
     * @param other - other operand to perform mod.
     */
    mod(other) {
        const otherBigInteger = other instanceof BigInteger ? other : BigInteger.fromNumber(other);
        return new BigInteger(__classPrivateFieldGet(this, _BigInteger_value, "f").mod(__classPrivateFieldGet(otherBigInteger, _BigInteger_value, "f")));
    }
    /**
     * Compares the two values and returns -1, 0, 1 if this value is larger, equal, smaller than the other value respectively.
     * @param other - other operand to compare against.
     */
    compare(other) {
        const otherBigInteger = other instanceof BigInteger ? other : BigInteger.fromNumber(other);
        return __classPrivateFieldGet(this, _BigInteger_value, "f").cmp(__classPrivateFieldGet(otherBigInteger, _BigInteger_value, "f"));
    }
    equals(other) {
        return this.compare(other) === 0;
    }
}
_BigInteger_value = new WeakMap();
//Memoization for twos
const twosComplementByteBounds = [
    [new BN(-128), new BN(127)],
    [new BN(-32768), new BN(32767)],
    [new BN(-8388608), new BN(8388607)],
];
function increaseBounds() {
    const nextBound = twosComplementByteBounds.length + 1;
    const power = new BN(nextBound * 8 - 1);
    twosComplementByteBounds.push([
        new BN(2).pow(power).neg(),
        new BN(2).pow(power).isubn(1),
    ]);
}
function isWithinBounds(value, bounds) {
    return value.gte(bounds[0]) && value.lte(bounds[1]);
}
function getBytesForTwos(value) {
    // This is standard hex length. Twos complement is always equal or larger.
    let byteLength = value.byteLength();
    if (byteLength === 0) {
        return 0;
    }
    while (byteLength <= 32) {
        if (twosComplementByteBounds.length < byteLength) {
            increaseBounds();
            continue;
        }
        const bounds = twosComplementByteBounds[byteLength - 1];
        if (isWithinBounds(value, bounds)) {
            return byteLength;
        }
        byteLength++;
    }
    throw new Error("Number too large to convert!");
}
/**
 * Converts a number to a Fixed8 format hex string
 * @param num - the value to convert
 * @param size - output size in bytes
 * @returns number in Fixed8 representation.
 *
 * @deprecated BigInteger.fromDecimal(num,8).toHex()
 */
export function num2fixed8(num, size = 8) {
    if (typeof num !== "number") {
        throw new Error(`num2fixed8 expected a number but got ${typeof num} instead.`);
    }
    if (size % 1 !== 0) {
        throw new Error(`num2fixed8 expected an integer for argument size but got ${size} instead.`);
    }
    const hexString = BigInteger.fromDecimal(num, 8).toReverseTwos();
    return hexString.padEnd(size * 2, "0");
}
/**
 * Converts a Fixed8 hex string to its original number
 * @param fixed8hex - number in Fixed8 representation
 *
 * @deprecated  parseInt(BigInteger.fromTwos(fixed8hex, true).toDecimal(8))
 */
export function fixed82num(fixed8hex) {
    return parseFloat(BigInteger.fromTwos(fixed8hex, true).toDecimal(8));
}
//# sourceMappingURL=BigInteger.js.map