UNPKG

fregejs

Version:

A propositional logic library written in Typescript

1,414 lines (1,382 loc) 64.6 kB
// src/exceptions/invalid-formula.exception.ts var InvalidFormulaException = class extends Error { }; // src/exceptions/invalid-inference.exception.ts var InferenceException = class extends Error { }; // src/exceptions/invalid-proof-sequence.exception.ts var InvalidProofSequenceException = class extends Error { }; // src/exceptions/unrecognized-token.exception.ts var UnrecognizedTokenException = class extends Error { }; // src/builder/Builder.ts var _Builder = class _Builder { static biconditional(left, right) { return `(${left} <-> ${right})`; } static conjunction(left, right) { return `(${left} \u2227 ${right})`; } static disjunction(left, right) { return `(${left} \u2228 ${right})`; } static implication(left, right) { return `(${left} -> ${right})`; } static buildRecursively(formula) { if (typeof formula === "string") return formula; if ("operation" in formula && formula.operation === "Negation") return `\xAC(${this.buildFormula(formula.value)})`; if (!("operation" in formula)) throw new InvalidFormulaException("Invalid Formula."); const left = this.buildFormula(formula.left); const right = this.buildFormula(formula.right); const operation = formula.operation; return this.operations[operation](left, right); } /** * Builds a formula with the syntax of logic. * @param formula - The logical formula to build. * @returns The builded logical formula. * @throws {InvalidFormulaException} */ static buildFormula(formula) { const result = this.buildRecursively(formula); return result; } }; _Builder.operations = { Biconditional: _Builder.biconditional, Conjunction: _Builder.conjunction, Disjunction: _Builder.disjunction, Implication: _Builder.implication }; var Builder = _Builder; // src/utils/isBinaryOperation.ts function isBinaryOperationFormula(formula) { return !!(formula.operation && formula.left && formula.right); } // src/utils/isBiconditional.ts function isBiconditional(formula) { return formula.operation === "Biconditional" && isBinaryOperationFormula(formula); } // src/utils/isConjunction.ts function isConjunction(formula) { return formula.operation === "Conjunction" && isBinaryOperationFormula(formula); } // src/utils/isDisjunction.ts function isDisjunction(formula) { return formula.operation === "Disjunction" && isBinaryOperationFormula(formula); } // src/utils/isImplication.ts function isImplication(formula) { return formula.operation === "Implication" && isBinaryOperationFormula(formula); } // src/utils/isContradiction.ts import { isDeepStrictEqual } from "util"; // src/utils/isNegation.ts function isNegation(formula) { return formula.operation === "Negation" && !!formula.value; } // src/utils/eliminateDoubleNegation.ts function eliminateDoubleNegations(formula) { if (isNegation(formula)) { const innerValue = formula.value; if (isNegation(innerValue)) { return eliminateDoubleNegations(innerValue.value); } return { operation: "Negation", value: eliminateDoubleNegations(innerValue) }; } if (isBinaryOperationFormula(formula)) { return { operation: formula.operation, left: eliminateDoubleNegations(formula.left), right: eliminateDoubleNegations(formula.right) }; } return formula; } // src/utils/isContradiction.ts function isContradiction(formula) { formula = eliminateDoubleNegations(formula); const firstCondition = isDeepStrictEqual(formula, { operation: "Conjunction", left: formula.left, right: { operation: "Negation", value: formula.left } }); const secondCondition = isDeepStrictEqual(formula, { operation: "Conjunction", left: { operation: "Negation", value: formula.right }, right: formula.right }); return firstCondition || secondCondition; } // src/utils/isArrayString.ts function isArrayString(array) { return array.every((item) => typeof item === "string"); } // src/utils/isHypothesis.ts function isHypothesis(x) { return x.type === "Hypothesis"; } // src/utils/isEndOfHypothesis.ts function isEndOfHypothesis(x) { return x.type === "End of Hypothesis"; } // src/utils/isMolecularFormula.ts function isMolecularFormula(formula) { if (typeof formula === "string") return false; if (formula.operation === "Negation") return false; return true; } // src/utils/isProofItemInferred.ts function isProofItemInferred(x) { if (!(x == null ? void 0 : x.type)) return false; return ["Knowledge", "End of Hypothesis", "Conclusion"].includes(x.type); } // src/utils/isPropositionalVariable.ts var propositionalVariablesList = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ]; function isPropositionalVariable(formula) { return typeof formula === "string" && propositionalVariablesList.includes(formula); } // src/utils/buildConjunctionString.ts function buildConjunctionString(premises) { const conjunctionFormulaArray = premises.map( (premise) => `(${premise})` ); const conjunctionFormulaString = conjunctionFormulaArray.join("\u2227"); return conjunctionFormulaString; } // src/utils/haveEvenNumberOfNegations.ts function haveEvenNumberOfNegations(formula) { if (!isNegation(formula)) return true; formula = eliminateDoubleNegations(formula); return !isNegation(formula); } // src/lexer/Lexer.ts var Lexer = class { /** * Creates a new Lexer instance. * @param formula The propositional logic formula to tokenize. */ constructor(formula) { this.tokens = []; this.pointer = 0; this.operator = ""; this.input = formula; } /** * Tokenizes the propositional logic formula. * @returns An array of tokens representing the formula. */ lex() { while (this.next()) { if (this.isSpecial(this.c)) { this.operator += this.c; if (this.operatorExists(this.operator)) { this.push({ type: "operator", value: this.operator }); this.operator = ""; } } else { if (this.operator) this.throwTokenException( this.operator, this.pointer - this.operator.length - 1 ); else if (this.isWhiteSpace(this.c)) continue; else if (this.isVariable(this.c)) this.push({ type: "variable", value: this.c }); else if (this.isExpressionBoundary(this.c)) this.push({ type: "boundary", value: this.c }); else this.throwTokenException(this.c, this.pointer - 2); } } return this.tokens; } next() { return this.c = this.input[this.pointer++]; } push(token) { this.tokens.push({ type: token.type, value: token.value }); } isWhiteSpace(c) { return /\s/.test(c); } isVariable(c) { return /[A-Z]/.test(c); } isSpecial(c) { return /[¬∧∨&!|\-><->]/.test(c); } isExpressionBoundary(c) { return /[\(\)]/.test(c); } operatorExists(op) { return ["\xAC", "!", "\u2227", "&", "\u2228", "|", "->", "<->"].indexOf(op) !== -1; } throwTokenException(token, position) { throw new UnrecognizedTokenException( `Unrecognized token "${token}" on position ${position}` ); } }; // src/exceptions/syntax-error.exception.ts var SyntaxException = class extends Error { }; // src/parser/Parser.ts var Parser = class { /** * Constructor of the Parser class. * @param tokens Array of tokens to be analyzed. */ constructor(tokens) { this.lastIsVariable = false; this.tokens = JSON.parse(JSON.stringify(tokens)); } /** * Performs the analysis of the tokens and returns the logical formula tree. * @returns The logical formula resulting from the analysis of the tokens. */ parse() { return this.process(); } process(operation) { var _a, _b, _c, _d, _e, _f; operation = operation || null; const args = []; while (this.next()) { if (this.lastIsVariable && ((_a = this.token) == null ? void 0 : _a.type) === "variable") throw new SyntaxException( `Token "${this.token.value}": Expected one variable, but received more than 1.` ); if (this.token === void 0) break; if (((_b = this.token) == null ? void 0 : _b.type) === "boundary") { if (this.token.value === ")") return this.node(operation, args); args.push(this.process()); } if (((_c = this.token) == null ? void 0 : _c.type) === "variable") { args.push(this.token.value); if (this.isUnary(operation)) return this.node(operation, args); } if (((_d = this.token) == null ? void 0 : _d.type) === "operator") { if (this.isUnary(this.token.value)) { args.push(this.process(this.token.value)); continue; } if (operation) { const tmp = args.slice(0); args.length = 0; args.push(this.node(operation, tmp)); } operation = this.token.value; } this.lastIsVariable = ((_e = this.token) == null ? void 0 : _e.type) === "variable" || this.lastIsVariable && ((_f = this.token) == null ? void 0 : _f.type) === "boundary"; } return this.node(operation, args); } next() { return this.token = this.tokens.shift(); } node(operator, args) { if (["->", "<->", "&", "|", "\u2227", "\u2228"].includes(operator)) { if (args.length !== 2) throw new SyntaxException(` Token "${operator}": expected 2 variables, but received 1. `); } if (operator === "\xAC" || operator === "!") return { operation: "Negation", value: args[0] }; if (operator === "\u2228" || operator === "|") return { operation: "Disjunction", left: args[0], right: args[1] }; if (operator === "\u2227" || operator === "&") return { operation: "Conjunction", left: args[0], right: args[1] }; if (operator === "->") return { operation: "Implication", left: args[0], right: args[1] }; if (operator === "<->") return { operation: "Biconditional", left: args[0], right: args[1] }; return args[0]; } isUnary(operator) { return operator === "\xAC" || operator === "!"; } }; // src/utils/parse.ts function parseToFormulaObject(formula) { const tokens = new Lexer(formula).lex(); return new Parser(tokens).parse(); } function parseToFormulaString(formula) { return Builder.buildFormula(formula); } // src/utils/printTruthTable.ts function printTruthTable(truthTable) { console.log(`\x1B[36m${truthTable.headers.join(" ")}\x1B[0m`); for (let i = 0; i < truthTable.truthCombinations.length; i++) { const combination = truthTable.truthCombinations[i]; const values = truthTable.truthValues[i]; const formattedCombination = combination.map((value) => value ? "\x1B[32mT\x1B[0m" : "\x1B[31mF\x1B[0m").join(" "); const formattedValue = values ? "\x1B[32mT\x1B[0m" : "\x1B[31mF\x1B[0m"; console.log(`${formattedCombination} ${formattedValue}`); } } // src/utils/objectBuilder.ts function implication(left, right) { let implication2 = { operation: "Implication", left, right }; return implication2; } function biconditional(left, right) { let biconditional2 = { operation: "Biconditional", left, right }; return biconditional2; } function conjunction(left, right) { let conjunction2 = { operation: "Conjunction", left, right }; return conjunction2; } function disjunction(left, right) { let disjunction2 = { operation: "Disjunction", left, right }; return disjunction2; } function negation(value) { let negation2 = { operation: "Negation", value }; return negation2; } // src/calculator/Calculator.ts var Calculator = class _Calculator { /** * Generates a truth table for the given formula. * * @param formula - The logical formula to generate a truth table for. * @param stringfiedFormula - An optional string representation of the formula. * @returns The truth table as an array containing headers, truth combinations, and results. * * @example * const output = Calculator.generateTruthTable('P -> Q'); * console.log(output); * // Output: * // { * // headers: ['P', 'Q', '(P -> Q)'], * // truthCombinations: [ * // [false, false], [false, true], * // [true, false], [true, true] * // ], * // truthValues: [true, true, false, true] * // } */ static generateTruthTable(formula, stringfiedFormula) { if (typeof formula === "string" && !isPropositionalVariable(formula)) { const parsedFormula = parseToFormulaObject(formula); return _Calculator.generateTruthTable(parsedFormula, formula); } const variables = /* @__PURE__ */ new Set(); _Calculator.collectVariables(formula, variables); const variableArray = Array.from(variables); const truthCombinations = _Calculator.generateTruthCombinations( variableArray.length ); const table = { headers: [], truthCombinations: [], truthValues: [] }; variableArray.forEach((variable) => { table.headers.push(variable); }); stringfiedFormula = stringfiedFormula || Builder.buildFormula(formula); table.headers.push(stringfiedFormula); truthCombinations.forEach((combination) => { const values = {}; variableArray.forEach((variable, index) => { values[variable] = !!combination[index]; }); table.truthCombinations.push(combination); const result = _Calculator.evaluate(formula, values); table.truthValues.push(result); }); return table; } /** * Evaluates the given logical formula with the provided truth values for variables. * * @param formula - The logical formula to evaluate. * @param values - An object representing truth values for propositional variables. * @returns The result of the evaluation (true or false). * * @example * const result = Calculator.evaluate('P -> Q', { P: true, Q: false }); * console.log(result); // Output: false */ static evaluate(formula, values) { if (typeof formula === "string" && !isPropositionalVariable(formula)) { const parsedFormula = parseToFormulaObject(formula); return _Calculator.evaluate(parsedFormula, values); } if (typeof formula === "string") return values[`${formula}`]; if (formula.operation === "Implication") { return _Calculator.evaluateImplication(formula, values); } if (formula.operation === "Biconditional") { return _Calculator.evaluateBiconditional(formula, values); } if (formula.operation === "Conjunction") { return _Calculator.evaluateConjunction(formula, values); } if (formula.operation === "Disjunction") { return _Calculator.evaluateDisjunction(formula, values); } if (formula.operation === "Negation") { return _Calculator.evaluateNegation(formula, values); } throw new Error("Invalid formula operation"); } /** * Checks if a given formula is a semantic consequence of the given premises. * A semantic consequence holds if, in every possible truth assignment to the propositional variables, * when all premises are true, the conclusion is also true. * * @param premises - An array of logical formulas or strings representing the premises. * @param conclusion - The conclusion formula to check as a semantic consequence. * @returns True if the conclusion is a semantic consequence of the premises, false otherwise. * * @example * const output = Calculator.isSemanticConsequence(['P->Q', 'P'], 'Q'); * console.log(output); // Output: true */ static isSemanticConsequence(premises, conclusion) { const variables = /* @__PURE__ */ new Set(); let conjunctionOfPremises; if (typeof conclusion === "string" && !isPropositionalVariable(conclusion)) conclusion = parseToFormulaObject(conclusion); if (premises.length === 1) { conjunctionOfPremises = typeof premises[0] === "object" ? premises[0] : parseToFormulaObject(premises[0]); } else { if (!isArrayString(premises)) { premises = premises.map((premise) => parseToFormulaString(premise)); } let conjunctionFormulaString = buildConjunctionString(premises); conjunctionOfPremises = parseToFormulaObject(conjunctionFormulaString); } _Calculator.collectVariables(conjunctionOfPremises, variables); const variableArray = Array.from(variables); const truthCombinations = _Calculator.generateTruthCombinations(variableArray.length); for (const combination of truthCombinations) { const values = {}; variableArray.forEach((variable, index) => { values[variable] = !!combination[index]; }); const allPremisesAreTrue = _Calculator.evaluate(conjunctionOfPremises, values); if (allPremisesAreTrue && !_Calculator.evaluate(conclusion, values)) return false; } return true; } static evaluateImplication(formula, values) { const left = _Calculator.evaluate(formula.left, values); const right = _Calculator.evaluate(formula.right, values); return !left || right; } static evaluateBiconditional(formula, values) { const left = _Calculator.evaluate(formula.left, values); const right = _Calculator.evaluate(formula.right, values); return left && right || !left && !right; } static evaluateConjunction(formula, values) { const left = _Calculator.evaluate(formula.left, values); const right = _Calculator.evaluate(formula.right, values); return left && right; } static evaluateDisjunction(formula, values) { const left = _Calculator.evaluate(formula.left, values); const right = _Calculator.evaluate(formula.right, values); return left || right; } static evaluateNegation(formula, values) { const value = _Calculator.evaluate(formula.value, values); return !value; } static generateTruthCombinations(numVariables) { const combinations = []; const totalCombinations = 2 ** numVariables; for (let i = 0; i < totalCombinations; i++) { const binaryString = i.toString(2).padStart(numVariables, "0"); const combination = binaryString.split("").map((bit) => bit === "1"); combinations.push(combination); } return combinations; } static collectVariables(formula, variables) { if (isPropositionalVariable(formula)) { variables.add(formula); } else if (isNegation(formula)) { _Calculator.collectVariables(formula.value, variables); } else { _Calculator.collectVariables(formula.left, variables); _Calculator.collectVariables(formula.right, variables); } } }; // src/rulers/RuleApplier.ts import { isDeepStrictEqual as isDeepStrictEqual3 } from "util"; // src/rulers/RuleSetter.ts import { isDeepStrictEqual as isDeepStrictEqual2 } from "util"; var RuleSetter = class { static BiconditionalIntroduction(conditional1, conditional2) { if (isDeepStrictEqual2(conditional1.left, conditional2.right) && isDeepStrictEqual2(conditional1.right, conditional2.left)) return { operation: "Biconditional", left: conditional1.left, right: conditional1.right }; const errorMsg = `Biconditional Introduction: cannot apply in ${parseToFormulaString( conditional1 )} and ${parseToFormulaString(conditional2)}`; throw new InferenceException(errorMsg); } static BiconditionalElimination(biconditional2) { const implication1 = { operation: "Implication", left: biconditional2.left, right: biconditional2.right }; const implication2 = { operation: "Implication", left: biconditional2.right, right: biconditional2.left }; return { operation: "Conjunction", left: implication1, right: implication2 }; } static ConditionalProof(hypothesis, conclusionOfHypothesis) { const conditional = { operation: "Implication", left: hypothesis, right: conclusionOfHypothesis }; return conditional; } static Conditionalization(formula, conditional) { if (isDeepStrictEqual2(formula, conditional.right)) return conditional; throw new InferenceException( `Conditionalization: cannot apply in ${parseToFormulaString( conditional )} with ${parseToFormulaString(formula)}` ); } static Commutativity(formula) { const right = formula.right; formula.right = formula.left; formula.left = right; return formula; } static Contraposition(formula) { const contraposition = { operation: "Implication", left: { operation: "Negation", value: formula.right }, right: { operation: "Negation", value: formula.left } }; return eliminateDoubleNegations(contraposition); } static ConjunctionIntroduction(formula1, formula2) { return { operation: "Conjunction", left: formula1, right: formula2 }; } static ConjunctionElimination(conjunction2) { return [conjunction2.left, conjunction2.right]; } static DeMorgan(formula) { if (isNegation(formula)) { if (isDisjunction(formula.value)) { return { operation: "Conjunction", left: { operation: "Negation", value: formula.value.left }, right: { operation: "Negation", value: formula.value.right } }; } if (isConjunction(formula.value)) { return { operation: "Disjunction", left: { operation: "Negation", value: formula.value.left }, right: { operation: "Negation", value: formula.value.right } }; } } if (isDisjunction(formula)) { if (!(isNegation(formula.left) && isNegation(formula.right))) throw new InferenceException( `De Morgan: cannot apply in ${parseToFormulaString(formula)}` ); return { operation: "Negation", value: { operation: "Conjunction", left: formula.left.value, right: formula.right.value } }; } if (isConjunction(formula)) { if (!(isNegation(formula.left) && isNegation(formula.right))) throw new InferenceException( `De Morgan: cannot apply in ${parseToFormulaString(formula)}` ); return { operation: "Negation", value: { operation: "Disjunction", left: formula.left.value, right: formula.right.value } }; } throw new InferenceException( `De Morgan: cannot apply in ${parseToFormulaString(formula)}` ); } static DisjunctionIntroduction(formula, disjunction2) { if (isDeepStrictEqual2(disjunction2.left, formula)) return disjunction2; if (isDeepStrictEqual2(disjunction2.right, formula)) return disjunction2; const errorMsg = `Disjunction Introduction: cannot apply in ${parseToFormulaString( disjunction2 )} with ${parseToFormulaString(formula)}`; throw new InferenceException(errorMsg); } static DisjunctiveSyllogism(disjunction2, negatedDisjunct) { if (isDeepStrictEqual2(disjunction2.left, negatedDisjunct.value)) return disjunction2.right; if (isDeepStrictEqual2(disjunction2.right, negatedDisjunct.value)) return disjunction2.left; const errorMsg = `Disjunctive Syllogism: cannot apply in ${parseToFormulaString( disjunction2 )} with ${parseToFormulaString(negatedDisjunct)}`; throw new InferenceException(errorMsg); } static ImplicationElimination(conditional) { return { operation: "Disjunction", left: { operation: "Negation", value: conditional.left }, right: conditional.right }; } static ImplicationNegation(negation2) { if (!isImplication(negation2.value)) throw new InferenceException( `Implication Negation: cannot apply in ${parseToFormulaString( negation2 )}` ); return { operation: "Conjunction", left: negation2.value.left, right: { operation: "Negation", value: negation2.value.right } }; } static DoubleNegation(formula) { return eliminateDoubleNegations(formula); } static DoubleNegationIntroduction(formula) { const negation2 = { operation: "Negation", value: { operation: "Negation", value: formula } }; return negation2; } static ConjunctionOverDisjunctionDistribution(formula) { return this.Distribute(formula, isDisjunction); } static DisjunctionOverConjunctionDistribution(formula) { return this.Distribute(formula, isConjunction); } static ConjunctionAssociativity(formula) { return this.Associate(formula, isConjunction); } static DisjunctionAssociativity(formula) { return this.Associate(formula, isDisjunction); } static BiconditionalAssociativity(formula) { return this.Associate(formula, isBiconditional); } static HypotheticalSyllogism(conditional1, conditional2) { if (isDeepStrictEqual2(conditional1.right, conditional2.left)) { return { operation: "Implication", left: conditional1.left, right: conditional2.right }; } if (isDeepStrictEqual2(conditional1.left, conditional2.right)) { return { operation: "Implication", left: conditional2.left, right: conditional1.right }; } const errorMsg = `Hypothetical Syllogism: cannot apply in ${parseToFormulaString( conditional1 )} with ${parseToFormulaString(conditional2)}`; throw new InferenceException(errorMsg); } static ModusPonens(conditional, antecedent) { if (isDeepStrictEqual2(conditional.left, antecedent)) return conditional.right; const errorMsg = `Modus Ponens: cannot apply in ${parseToFormulaString( conditional )} with ${parseToFormulaString(antecedent)}`; throw new InferenceException(errorMsg); } static ModusTollens(conditional, negatedConsequent) { if (isDeepStrictEqual2(conditional.right, negatedConsequent.value)) return { operation: "Negation", value: conditional.left }; const errorMsg = `Modus Tollens: cannot apply in ${parseToFormulaString( conditional )} with ${parseToFormulaString(negatedConsequent)}`; throw new InferenceException(errorMsg); } static ReductioAdAbsurdum(conditional) { if (isContradiction(conditional.right)) return { operation: "Negation", value: conditional.left }; throw new InferenceException( `Reductio Ad Absurdum: cannot apply in ${conditional} with ${conditional.right}` ); } static DistributeRecursively(formula, isK) { try { return this.Distribute(formula, isK); } catch (e) { return formula; } } static Distribute(formula, isK) { let KFormula; let otherFormula; if (isK(formula.left)) { KFormula = formula.left; otherFormula = formula.right; } else if (isK(formula.right)) { KFormula = formula.right, otherFormula = formula.left; } else { throw new InferenceException( `Distribution: cannot apply in ${parseToFormulaString(formula)}` ); } let distributedFormula = { operation: KFormula.operation, left: { operation: formula.operation, left: otherFormula, right: KFormula.left }, right: { operation: formula.operation, left: otherFormula, right: KFormula.right } }; if (!isPropositionalVariable(distributedFormula.left)) distributedFormula.left = this.DistributeRecursively( distributedFormula.left, isK ); if (!isPropositionalVariable(distributedFormula.right)) distributedFormula.right = this.DistributeRecursively( distributedFormula.right, isK ); return distributedFormula; } static Associate(formula, isT) { let mainFormula; let otherFormula; if (isT(formula.left)) { mainFormula = formula.left; otherFormula = formula.right; return { operation: formula.operation, left: mainFormula.left, right: { operation: formula.operation, left: mainFormula.right, right: otherFormula } }; } if (isT(formula.right)) { mainFormula = formula.right; otherFormula = formula.left; return { operation: formula.operation, left: { operation: formula.operation, left: otherFormula, right: mainFormula.left }, right: mainFormula.right }; } throw new InferenceException( `Associativity: cannot apply in ${parseToFormulaString(formula)}` ); } }; // src/rulers/RuleApplier.ts var RuleApplier = class _RuleApplier extends RuleSetter { static biconditionalIntroduction(item, proof) { const requiredItens = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 2, requiredItens.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItens, proof); const formulas = [ proof[requiredItens[0]].expression, proof[requiredItens[1]].expression ]; if (!isImplication(formulas[0]) || !isImplication(formulas[1])) throw new InferenceException( `Biconditional Introduction (Line ${line}): conditionals not found.` ); const inferenceResult = _RuleApplier.BiconditionalIntroduction( formulas[0], formulas[1] ); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static biconditionalElimination(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const biconditional2 = proof[requiredItem[0]].expression; if (!isBiconditional(biconditional2)) throw new InferenceException( `Biconditional Elimination (Line ${line}): biconditional not found.` ); const inferenceResult = _RuleApplier.BiconditionalElimination(biconditional2); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static conditionalization(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); if (!isImplication(item.expression)) throw new InferenceException( `Conditionalization (Line ${line}): the formula is not an implication.` ); const formula = proof[requiredItem[0]].expression; if (typeof formula === "string" && !isPropositionalVariable(formula)) throw new InferenceException( `Conditionalization (Line ${line}): formula not found.` ); const inferenceResult = _RuleApplier.Conditionalization( formula, item.expression ); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static conjunctionIntroduction(item, proof) { const requiredItens = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 2, requiredItens.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItens, proof); const firstFormula = proof[requiredItens[0]].expression; if (typeof firstFormula === "string" && !isPropositionalVariable(firstFormula)) throw new InferenceException( `Conjunction Introduction (Line ${line}): line ${requiredItens[0]} formula not found.` ); const secondFormula = proof[requiredItens[1]].expression; if (typeof secondFormula === "string" && !isPropositionalVariable(secondFormula)) throw new InferenceException( `Conjunction Introduction (Line ${line}): line ${requiredItens[1]} formula not found.` ); const inferenceResult = _RuleApplier.ConjunctionIntroduction( firstFormula, secondFormula ); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static conjunctionElimination(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const conjunction2 = proof[requiredItem[0]].expression; if (!isConjunction(conjunction2)) throw new InferenceException( `Conjunction Elimination (Line ${line}): conjunction not found.` ); const inferenceResults = _RuleApplier.ConjunctionElimination(conjunction2); if (!isDeepStrictEqual3(item.expression, inferenceResults[0]) && !isDeepStrictEqual3(item.expression, inferenceResults[1])) { throw new InferenceException( `Conjunction Elimination (Line ${line}): expected ${parseToFormulaString( inferenceResults[0] )} or ${parseToFormulaString( inferenceResults[1] )} but received ${parseToFormulaString(item.expression)}` ); } return inferenceResults; } static commutativity(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const formula = proof[requiredItem[0]].expression; if (!isDisjunction(formula) && !isConjunction(formula) && !isBiconditional(formula)) throw new InferenceException( `Commutativity (Line ${line}): cannot find any conjunction, biconditional or disjunction.` ); const inferenceResult = _RuleApplier.Commutativity(formula); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static contraposition(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const formula = proof[requiredItem[0]].expression; if (!isImplication(formula)) throw new InferenceException( `Contraposition (Line ${line}): implication not found.` ); const inferenceResult = _RuleApplier.Contraposition(formula); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static deMorgan(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const formula = proof[requiredItem[0]].expression; if (!isNegation(formula) && !isConjunction(formula) && !isDisjunction(formula)) throw new InferenceException( `De Morgan (Line ${line}): formula is not a disjunction, conjunction or negation.` ); const inferenceResult = _RuleApplier.DeMorgan(formula); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static disjunctionIntroduction(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); if (!isDisjunction(item.expression)) throw new InferenceException( `Disjunction Introduction (Line ${line}): expression is not a disjunction` ); const formula = proof[requiredItem[0]].expression; if (typeof formula === "string" && !isPropositionalVariable(formula)) throw new InferenceException( `Disjunction Introduction (Line ${line}): formula not found.` ); const inferenceResult = _RuleApplier.DisjunctionIntroduction( formula, item.expression ); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static disjunctiveSyllogism(item, proof) { const requiredItens = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 2, requiredItens.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItens, proof); const firstFormula = proof[requiredItens[0]].expression; const secondFormula = proof[requiredItens[1]].expression; let remainingFormula; let disjunction2; if (isDisjunction(firstFormula)) { disjunction2 = firstFormula; remainingFormula = secondFormula; } if (isDisjunction(secondFormula)) { disjunction2 = secondFormula; remainingFormula = firstFormula; } if (!disjunction2) throw new InferenceException( `Disjunctive Syllogism (Line ${line}): disjunction not found` ); let negation2; if (isNegation(remainingFormula)) negation2 = remainingFormula; if (!negation2) throw new InferenceException( `Disjunctive Syllogism (Line ${line}): negation not found` ); const inferenceResult = _RuleApplier.DisjunctiveSyllogism( disjunction2, negation2 ); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static doubleNegation(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const formula = proof[requiredItem[0]].expression; if (typeof formula === "string" && !isPropositionalVariable(formula)) throw new InferenceException( `Double Negation (Line ${line}): formula not found.` ); const inferenceResult = _RuleApplier.DoubleNegation(formula); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static doubleNegationIntroduction(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const formula = proof[requiredItem[0]].expression; if (typeof formula === "string" && !isPropositionalVariable(formula)) throw new InferenceException( `Double Negation Introduction (Line ${line}): negation not found.` ); const inferenceResult = _RuleApplier.DoubleNegationIntroduction(formula); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static hypotheticalSyllogism(item, proof) { const requiredItens = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 2, requiredItens.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItens, proof); const firstFormula = proof[requiredItens[0]].expression; const secondFormula = proof[requiredItens[1]].expression; if (!isImplication(firstFormula) || !isImplication(secondFormula)) throw new InferenceException( `Hypothetical Syllogism (Line ${line}): both formulas should be conditionals.` ); const inferenceResult = _RuleApplier.HypotheticalSyllogism( firstFormula, secondFormula ); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static implicationElimination(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const formula = proof[requiredItem[0]].expression; if (!isImplication(formula)) throw new InferenceException( `Implication Elimination (Line ${line}): implication not found.` ); const inferenceResult = _RuleApplier.ImplicationElimination(formula); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static implicationNegation(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); let formula = proof[requiredItem[0]].expression; if (!isNegation(formula)) throw new InferenceException( `Implication Negation (Line ${line}): negation not found` ); const inferenceResult = _RuleApplier.ImplicationNegation(formula); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static modusPonens(item, proof) { const requiredItens = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 2, requiredItens.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItens, proof); let firstFormula = proof[requiredItens[0]].expression; let secondFormula = proof[requiredItens[1]].expression; let remainingFormula; let implication2; if (isImplication(firstFormula)) { implication2 = firstFormula; remainingFormula = secondFormula; } if (isImplication(secondFormula)) { implication2 = secondFormula; remainingFormula = firstFormula; } if (!implication2) throw new InferenceException( `Modus Ponens (Line ${line}): implication not found` ); const antecedent = remainingFormula; if (!isPropositionalVariable(antecedent) && typeof antecedent === "string") throw new InferenceException( `Modus Ponens (Line ${line}): antecedent not found` ); const inferenceResult = _RuleApplier.ModusPonens(implication2, antecedent); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static modusTollens(item, proof) { const requiredItens = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 2, requiredItens.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItens, proof); const firstFormula = proof[requiredItens[0]].expression; const secondFormula = proof[requiredItens[1]].expression; let remainingFormula; let implication2; if (isImplication(firstFormula)) { implication2 = firstFormula; remainingFormula = secondFormula; } if (isImplication(secondFormula)) { implication2 = secondFormula; remainingFormula = firstFormula; } if (!implication2) throw new InferenceException( `Modus Tollens (Line ${line}): implication not found` ); const consequent = remainingFormula; if (!isNegation(consequent)) throw new InferenceException( `Modus Tollens (Line ${line}): negated consequent not found` ); const inferenceResult = _RuleApplier.ModusTollens(implication2, consequent); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static conjunctionOverDisjunctionDistribution(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const conjunction2 = proof[requiredItem[0]].expression; if (!isConjunction(conjunction2)) throw new InferenceException( `Distribution (Line ${line}): conjunction not found.` ); const inferenceResult = _RuleApplier.ConjunctionOverDisjunctionDistribution(conjunction2); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static disjunctionOverConjunctionDistribution(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const disjunction2 = proof[requiredItem[0]].expression; if (!isDisjunction(disjunction2)) throw new InferenceException( `Distribution (Line ${line}): disjunction not found.` ); const inferenceResult = _RuleApplier.DisjunctionOverConjunctionDistribution(disjunction2); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static conjunctionAssociativity(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const conjunction2 = proof[requiredItem[0]].expression; if (!isConjunction(conjunction2)) throw new InferenceException( `Associativity (Line ${line}): conjunction not found.` ); const inferenceResult = _RuleApplier.ConjunctionAssociativity(conjunction2); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static disjunctionAssociativity(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const disjunction2 = proof[requiredItem[0]].expression; if (!isDisjunction(disjunction2)) throw new InferenceException( `Associativity (Line ${line}): disjunction not found.` ); const inferenceResult = _RuleApplier.DisjunctionAssociativity(disjunction2); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static biconditionalAssociativity(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const biconditional2 = proof[requiredItem[0]].expression; if (!isBiconditional(biconditional2)) throw new InferenceException( `Associativity (Line ${line}): disjunction not found.` ); const inferenceResult = _RuleApplier.BiconditionalAssociativity(biconditional2); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static conditionalProof(item, proof) { const requiredItens = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 2, requiredItens.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItens, proof); const item1 = proof[requiredItens[0]]; const item2 = proof[requiredItens[1]]; if (!isPropositionalVariable(item2.expression) && typeof item2.expression === "string") throw new InferenceException( `Conditional Proof (Line ${line}): cannot find a formula at line ${item2.id}` ); if (!isPropositionalVariable(item1.expression) && typeof item1.expression === "string") throw new InferenceException( `Conditional Proof (Line ${line}): cannot find a formula at line ${item1.id}` ); let hypothesis; let endOfHypothesis; if (isHypothesis(item1) && isEndOfHypothesis(item2)) { hypothesis = item1.expression; if (item2.hypothesisId != item1.id) throw new InferenceException( `Conditional Proof (Line ${line}): end of hypothesis references line ${item2.hypothesisId} hypothesis, but received line ${item1.id} hypothesis` ); endOfHypothesis = item2.expression; } else if (isHypothesis(item2) && isEndOfHypothesis(item1)) { hypothesis = item2.expression; if (item1.hypothesisId != item2.id) throw new InferenceException( `Conditional Proof (Line ${line}): end of hypothesis references line ${item1.hypothesisId} hypothesis, but received line ${item2.id} hypothesis` ); endOfHypothesis = item1.expression; } else { throw new InferenceException( `Conditional Proof: end of hypothesis or hypothesis not found.` ); } const inferenceResult = _RuleApplier.ConditionalProof( hypothesis, endOfHypothesis ); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static reductioAdAbsurdum(item, proof) { const requiredItem = item.from[0]; const line = item.id; _RuleApplier.throwsIfLengthDoesntMatch(item, 1, requiredItem.length); _RuleApplier.throwsIfIndexDoesntExist(requiredItem, proof); const conditional = proof[requiredItem[0]].expression; if (!isImplication(conditional)) throw new InferenceException( `Reductio Ad Absurdum (Line ${line}): conditional not found.` ); const inferenceResult = _RuleApplier.ReductioAdAbsurdum(conditional); _RuleApplier.throwsIfIsNotEqual(inferenceResult, item); return inferenceResult; } static throwsIfIsNotEqual(expectedFormula, actualItem) { const actualFormula = actualItem.expression; const inferenceMethod = actualItem.from[1]; if (!isDeepStrictEqual3(expectedFormula, actualFormula)) { throw new InferenceException(` ${inferenceMethod} (Line ${actualItem.id}): expected ${parseToFormulaString( expectedFormula )} but received ${parseToFormulaString(actualFormula)} `); } } static throwsIfLengthDoesntMatch(item, expected, received) { const rule = item.from[1]; const line = item.id; if (expected !== received) throw new InferenceException( `${rule} (Line ${line}): expect ${expected} formulas to apply the rule but received ${received}.` ); } static throwsIfIndexDoesntExist(requiredItens, proof) { requiredItens.forEach((idx) => { if (!proof[idx]) throw new InferenceException(`Cannot find a formula at index ${idx}`); }); } }; // src/types/syntactic/proof.ts var inferenceRulesMap = { "Associativity (Biconditional)": RuleApplier.biconditionalAssociativity, "Associativity (Conjunction)": RuleApplier.conjunctionAssociativity, "Associativity (Disjuncti