/**
 * Copyright (C) 2024 Hedera Hashgraph, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the ""License"");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an ""AS IS"" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { MathEx } from '../../../../src/core/util/math_ex.js';
import { ArithmeticError } from '../../../../src/core/util/arithmetic_error.js';
describe('MathEx', () => {
    it('testLongExact', () => {
        testLongExactTwice(0, 0);
        testLongExactTwice(1, 1);
        testLongExactTwice(1, -1);
        testLongExactTwice(1000, 2000);
        testLongExactTwice(Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
        testLongExactTwice(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
        testLongExactTwice(Number.MIN_SAFE_INTEGER, 1);
        testLongExactTwice(Number.MAX_SAFE_INTEGER, 1);
        testLongExactTwice(Number.MIN_SAFE_INTEGER, 2);
        testLongExactTwice(Number.MAX_SAFE_INTEGER, 2);
        testLongExactTwice(Number.MIN_SAFE_INTEGER, -1);
        testLongExactTwice(Number.MAX_SAFE_INTEGER, -1);
        testLongExactTwice(Number.MIN_SAFE_INTEGER, -2);
        testLongExactTwice(Number.MAX_SAFE_INTEGER, -2);
        testLongExactTwice(Math.trunc(Number.MIN_SAFE_INTEGER / 2), 2);
        testLongExactTwice(Math.trunc(Number.MAX_SAFE_INTEGER / 2), 2);
    });
    it('testLongFloorDivMod', () => {
        testLongFloorDivMod(4, 0, 0, 0, true, true);
        testLongFloorDivMod(4, 3, 1, 1, false, false);
        testLongFloorDivMod(3, 3, 1, 0, false, false);
        testLongFloorDivMod(2, 3, 0, 2, false, false);
        testLongFloorDivMod(1, 3, 0, 1, false, false);
        testLongFloorDivMod(0, 3, 0, 0, false, false);
        testLongFloorDivMod(4, -3, -2, -2, false, false);
        testLongFloorDivMod(3, -3, -1, 0, false, false);
        testLongFloorDivMod(2, -3, -1, -1, false, false);
        testLongFloorDivMod(1, -3, -1, -2, false, false);
        testLongFloorDivMod(0, -3, 0, 0, false, false);
        testLongFloorDivMod(-1, 3, -1, 2, false, false);
        testLongFloorDivMod(-2, 3, -1, 1, false, false);
        testLongFloorDivMod(-3, 3, -1, 0, false, false);
        testLongFloorDivMod(-4, 3, -2, 2, false, false);
        testLongFloorDivMod(-1, -3, 0, -1, false, false);
        testLongFloorDivMod(-2, -3, 0, -2, false, false);
        testLongFloorDivMod(-3, -3, 1, 0, false, false);
        testLongFloorDivMod(-4, -3, 1, -1, false, false);
        testLongFloorDivMod(Number.MAX_SAFE_INTEGER, 1, Number.MAX_SAFE_INTEGER, 0, false, false);
        testLongFloorDivMod(Number.MAX_SAFE_INTEGER, -1, -Number.MAX_SAFE_INTEGER, 0, false, false);
        testLongFloorDivMod(Number.MAX_SAFE_INTEGER, 3, Math.floor(Number.MAX_SAFE_INTEGER / 3), 1, false, false);
        testLongFloorDivMod(Number.MAX_SAFE_INTEGER - 1, 3, Math.floor(Number.MAX_SAFE_INTEGER - 1) / 3, 0, false, false);
        testLongFloorDivMod(Number.MIN_SAFE_INTEGER, 3, Math.floor(Number.MIN_SAFE_INTEGER / 3), 1, false, false);
        testLongFloorDivMod(Number.MIN_SAFE_INTEGER + 1, 3, Math.floor(Number.MIN_SAFE_INTEGER / 3) + 1, 0, false, false);
        testLongFloorDivMod(Number.MIN_SAFE_INTEGER + 1, -1, Number.MAX_SAFE_INTEGER - 1, 0, false, false);
        // Special case of integer overflow
        testLongFloorDivMod(Number.MIN_SAFE_INTEGER, -1, Number.MAX_SAFE_INTEGER, 0, false, false);
    });
});
function testLongExact(x, y) {
    let resultBig;
    try {
        resultBig = BigInt(x) + BigInt(y);
        const sum = MathEx.addExact(x, y);
        checkResult('addExact', x, y, sum, resultBig);
    }
    catch (e) {
        checkError('addExact', x, y, resultBig, e);
    }
    try {
        resultBig = BigInt(x) - BigInt(y);
        const diff = MathEx.subtractExact(x, y);
        checkResult('subtractExact', x, y, diff, resultBig);
    }
    catch (e) {
        checkError('subtractExact', x, y, resultBig, e);
    }
    try {
        resultBig = BigInt(x) * BigInt(y);
        const product = MathEx.multiplyExact(x, y);
        checkResult('multiplyExact', x, y, product, resultBig);
    }
    catch (e) {
        checkError('multiplyExact', x, y, resultBig, e);
    }
}
function testLongExactTwice(x, y) {
    testLongExact(x, y);
    testLongExact(y, x);
}
function checkError(message, x, y, resultBig, e) {
    if (!(e instanceof ArithmeticError)) {
        throw e;
    }
    if (Number.isSafeInteger(Number(resultBig))) {
        expect.fail(`${message}(${x}, ${y}); Unexpected exception: ${e}`);
    }
}
function checkResult(message, x, y, result, expected) {
    const resultBig = BigInt(result);
    const finalMessage = `${message}(${x}, ${y}) = ${result}`;
    if (!Number.isSafeInteger(Number(resultBig))) {
        expect.fail(`${finalMessage}; expected an arithmetic error`);
    }
    else if (resultBig !== expected) {
        expect.fail(`${finalMessage}; expected ${expected}`);
    }
}
function testLongFloorDivMod(x, y, divExpected, modExpected, divThrows, modThrows) {
    testLongFloorDiv(x, y, divExpected, divThrows);
    testLongFloorMod(x, y, modExpected, modThrows);
}
function testLongFloorDiv(x, y, expected, shouldThrow) {
    try {
        const result = MathEx.floorDiv(x, y);
        if (result !== expected) {
            expect.fail(`floorDiv(${x}, ${y}) = ${result}; expected ${expected}`);
        }
        if (shouldThrow) {
            expect.fail(`floorDiv(${x}, ${y}); expected an arithmetic error`);
        }
    }
    catch (e) {
        if (!(e instanceof ArithmeticError)) {
            throw e;
        }
        if (!shouldThrow) {
            expect.fail(`floorDiv(${x}, ${y}); Unexpected exception: ${e}`);
        }
    }
}
function testLongFloorMod(x, y, expected, shouldThrow) {
    try {
        const result = MathEx.floorMod(x, y);
        if (result !== expected) {
            expect.fail(`floorMod(${x}, ${y}) = ${result}; expected ${expected}`);
        }
        if (shouldThrow) {
            expect.fail(`floorMod(${x}, ${y}); expected an arithmetic error`);
        }
    }
    catch (e) {
        if (!(e instanceof ArithmeticError)) {
            throw e;
        }
        if (e instanceof ArithmeticError && y !== 0) {
            expect.fail(`floorMod(${x}, ${y}); Unexpected arithmetic error: ${e}`);
        }
        if (!shouldThrow) {
            expect.fail(`floorMod(${x}, ${y}); Unexpected exception: ${e}`);
        }
    }
}
//# sourceMappingURL=math_ex.test.js.map