/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { makeAnonChange } from "./changeRebaser";
export const noFailure = {
    rebaseLeftDistributivity: [],
    rebaseRightDistributivity: [],
    composeAssociativity: [],
    rebaseOverDoUndoPairIsNoOp: [],
    rebaseOverUndoRedoPairIsNoOp: [],
    composeWithInverseIsNoOp: [],
    composeWithEmptyIsNoOp: [],
    rebaseOverEmptyIsNoOp: [],
    rebaseEmptyIsEmpty: [],
    emptyInverseIsEmpty: [],
};
/**
 * Verifies the axioms of `ChangeRebaser` are met by the given `rebaser`.
 * @param rebaser - The rebaser to test.
 * @param changes - The set of changes to use for testing the `rebaser`.
 * @param isEquivalent - Used to compare whether two changes are equivalent for the purposes of this axioms.
 */
export function verifyChangeRebaser(rebaser, changes, isEquivalent) {
    const rebase = (change, over) => rebaser.rebase(change, makeAnonChange(over));
    const compose = (changeToCompose) => rebaser.compose(changeToCompose.map(makeAnonChange));
    // TODO: test with isRollback = true
    const invert = (change) => rebaser.invert(makeAnonChange(change), false);
    const output = {
        rebaseLeftDistributivity: [],
        rebaseRightDistributivity: [],
        composeAssociativity: [],
        rebaseOverDoUndoPairIsNoOp: [],
        rebaseOverUndoRedoPairIsNoOp: [],
        composeWithInverseIsNoOp: [],
        composeWithEmptyIsNoOp: [],
        rebaseOverEmptyIsNoOp: [],
        rebaseEmptyIsEmpty: [],
        emptyInverseIsEmpty: [],
    };
    for (const changeA of changes) {
        const requirement1 = isComposeWithInverseEqualsEmpty(changeA);
        if (requirement1 !== true) {
            output.composeWithInverseIsNoOp.push(requirement1);
        }
        const requirement2 = isComposeWithEmptyNoOp(changeA);
        if (requirement2 !== true) {
            output.composeWithEmptyIsNoOp.push(requirement2);
        }
        const requirement3 = isRebaseOverEmptyNoOp(changeA);
        if (requirement3 !== true) {
            output.rebaseOverEmptyIsNoOp.push(requirement3);
        }
        const requirement4 = isRebaseEmptyEmpty(changeA);
        if (requirement4 !== true) {
            output.rebaseEmptyIsEmpty.push(requirement4);
        }
        const requirement5 = isEmptyInverseEmpty(changeA);
        if (requirement5 !== true) {
            output.emptyInverseIsEmpty.push(requirement5);
        }
        for (const changeB of changes) {
            const requirement6 = isRebaseOverDoUndoPairNoOp(changeA, changeB);
            if (requirement6 !== true) {
                output.rebaseOverDoUndoPairIsNoOp.push(requirement6);
            }
            const requirement7 = isRebaseOverUndoRedoPairNoOp(changeA, changeB);
            if (requirement7 !== true) {
                output.rebaseOverUndoRedoPairIsNoOp.push(requirement7);
            }
            for (const changeC of changes) {
                const requirement8 = isRebaseLeftDistributive(changeA, changeB, changeC);
                if (requirement8 !== true) {
                    output.rebaseLeftDistributivity.push(requirement8);
                }
                const requirement9 = isComposeAssociative(changeA, changeB, changeC);
                if (requirement9 !== true) {
                    output.composeAssociativity.push(requirement9);
                }
                const requirement10 = isRebaseRightDistributive(changeA, changeB, changeC);
                if (requirement10 !== true) {
                    output.rebaseRightDistributivity.push(requirement10);
                }
            }
        }
    }
    return output;
    // Requirement testing the rebasing of composed changes and rebased changes.
    function isRebaseLeftDistributive(changeA, changeB, changeC) {
        try {
            const rebaseChangeset1 = rebase(changeA, compose([changeB, changeC]));
            const rebaseChangeset2 = rebase(rebase(changeA, changeB), changeC);
            const equivalent = isEquivalent(rebaseChangeset1, rebaseChangeset2);
            if (equivalent) {
                return true;
            }
            return {
                type: "Violation",
                case: [changeA, changeB, changeC],
            };
        }
        catch (error) {
            return {
                type: "Error",
                case: [changeA, changeB, changeC],
                error,
            };
        }
    }
    // Requirement checking different ordering of composed changes
    function isComposeAssociative(changeA, changeB, changeC) {
        try {
            const changeset1 = compose([changeA, compose([changeB, changeC])]);
            const changeset2 = compose([compose([changeA, changeB]), changeC]);
            const changeset3 = compose([changeA, changeB, changeC]);
            const equivalent = isEquivalent(changeset1, changeset2) && isEquivalent(changeset1, changeset3);
            if (equivalent) {
                return true;
            }
            return {
                type: "Violation",
                case: [changeA, changeB, changeC],
            };
        }
        catch (error) {
            return {
                type: "Error",
                case: [changeA, changeB, changeC],
                error,
            };
        }
    }
    function isRebaseRightDistributive(changeA, changeB, changeC) {
        try {
            const changeset1 = rebase(compose([changeA, changeB]), changeC);
            const changeset2 = compose([
                rebase(changeA, changeC),
                rebase(changeB, compose([invert(changeA), changeC, rebase(changeA, changeC)])),
            ]);
            const equivalent = isEquivalent(changeset1, changeset2);
            if (equivalent) {
                return true;
            }
            return {
                type: "Violation",
                case: [changeA, changeB, changeC],
            };
        }
        catch (error) {
            return {
                type: "Error",
                case: [changeA, changeB, changeC],
                error,
            };
        }
    }
    // requirement for do-undo pair
    function isRebaseOverDoUndoPairNoOp(changeA, changeB) {
        try {
            const inv = invert(changeB);
            const r1 = rebase(changeA, changeB);
            const r2 = rebase(r1, inv);
            const equivalent = isEquivalent(r2, changeA);
            if (equivalent) {
                return true;
            }
            return {
                type: "Violation",
                case: [changeA, changeB],
            };
        }
        catch (error) {
            return {
                type: "Error",
                case: [changeA, changeB],
                error,
            };
        }
    }
    // requirement for sandwich rebasing
    function isRebaseOverUndoRedoPairNoOp(changeA, changeB) {
        try {
            const invB = invert(changeB);
            const r1 = rebase(changeA, changeB);
            const r2 = rebase(r1, invB);
            const r3 = rebase(r2, changeB);
            const equivalent = isEquivalent(r3, r1);
            if (equivalent) {
                return true;
            }
            return {
                type: "Violation",
                case: [changeA, changeB],
            };
        }
        catch (error) {
            return {
                type: "Error",
                case: [changeA, changeB],
                error,
            };
        }
    }
    // requirement for compose of a change with it's inverse.
    function isComposeWithInverseEqualsEmpty(changeA) {
        try {
            const changeset = compose([changeA, invert(changeA)]);
            const equivalent = isEquivalent(changeset, compose([]));
            return equivalent ? true : { type: "Violation", case: changeA };
        }
        catch (error) {
            return {
                type: "Error",
                case: changeA,
                error,
            };
        }
    }
    // compose([ε, A]) => A && compose([A, ε]) => A
    function isComposeWithEmptyNoOp(changeA) {
        try {
            const noOp = compose([]);
            const changeset1 = compose([changeA, noOp]);
            const changeset2 = compose([noOp, changeA]);
            const equivalent = isEquivalent(changeset1, changeset2) && isEquivalent(changeset1, changeA);
            return equivalent ? true : { type: "Violation", case: changeA };
        }
        catch (error) {
            return {
                type: "Error",
                case: changeA,
                error,
            };
        }
    }
    // rebase(A, ε) => A
    function isRebaseOverEmptyNoOp(changeA) {
        try {
            const noOp = compose([]);
            const changeset = rebase(changeA, noOp);
            const equivalent = isEquivalent(changeset, changeA);
            return equivalent ? true : { type: "Violation", case: changeA };
        }
        catch (error) {
            return {
                type: "Error",
                case: changeA,
                error,
            };
        }
    }
    // rebase(ε, A) => ε
    function isRebaseEmptyEmpty(changeA) {
        try {
            const noOp = compose([]);
            const changeset = rebase(noOp, changeA);
            const equivalent = isEquivalent(changeset, noOp);
            return equivalent ? true : { type: "Violation", case: changeA };
        }
        catch (error) {
            return {
                type: "Error",
                case: changeA,
                error,
            };
        }
    }
    // invert(ε) => ε
    function isEmptyInverseEmpty(changeA) {
        try {
            const noOp = compose([]);
            const changeset = invert(noOp);
            const equivalent = isEquivalent(changeset, noOp);
            return equivalent ? true : { type: "Violation", case: changeA };
        }
        catch (error) {
            return {
                type: "Error",
                case: changeA,
                error,
            };
        }
    }
}
//# sourceMappingURL=verifyChangeRebaser.js.map