fregejs
Version:
A propositional logic library written in Typescript
1,414 lines (1,382 loc) • 64.6 kB
JavaScript
// 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