import {
MathematicalArgument,
MathematicalTerm,
MathematicalCondition,
LogicResult
} from '../../types.js';
import { SequencePatternRecognizer } from '../patternRecognition.js';
import { MathematicalEvaluator } from './MathematicalEvaluator.js';
/**
* Mathematical solver
* Handles solving mathematical problems, equations, and sequences
*/
export class MathematicalSolver {
private patternRecognizer: SequencePatternRecognizer;
private evaluator: MathematicalEvaluator;
constructor() {
this.patternRecognizer = new SequencePatternRecognizer();
this.evaluator = new MathematicalEvaluator();
}
/**
* Generate solution for mathematical problem
* @param argument The mathematical argument
* @param variableMap Variable mappings
* @param sequenceMap Sequence mappings
* @returns Solution result
*/
generateSolution(
argument: MathematicalArgument,
variableMap: Map<string, string> = new Map(),
sequenceMap: Map<string, number[]> = new Map()
): LogicResult {
const steps: any[] = [];
let conclusion = '';
// Add variable mapping if available
if (variableMap.size > 0 || sequenceMap.size > 0) {
let mappingExplanation = "Mathematical entities identified:";
if (variableMap.size > 0) {
mappingExplanation += "\n\nVariables:";
for (const [statement, variable] of variableMap.entries()) {
mappingExplanation += `\n• ${variable}: ${statement}`;
}
}
if (sequenceMap.size > 0) {
mappingExplanation += "\n\nSequences:";
for (const [name, values] of sequenceMap.entries()) {
mappingExplanation += `\n• ${name}: ${values.join(', ')}`;
}
}
// Add mapping as first step
steps.push({
index: 1,
statement: mappingExplanation,
rule: 'Entity Identification',
justification: 'Natural language translated to mathematical entities'
});
}
// Solve sequences from constraints
const sequences = this.extractSequencesFromConstraints(argument.constraints);
for (const {name, values} of sequences) {
const pattern = this.patternRecognizer.detectPattern(values);
if (pattern) {
const nextValue = this.patternRecognizer.predictNext(values, pattern);
steps.push({
index: steps.length + 1,
statement: `Sequence "${values.join(', ')}" follows pattern: ${pattern.formula}`,
rule: `${pattern.type} pattern`,
justification: `Next value: ${nextValue}`
});
if (!conclusion) {
conclusion = `The next value in the sequence is ${nextValue}`;
}
}
}
// Solve constraints
const constraintSolutions = this.solveConstraints(argument.constraints);
for (const solution of constraintSolutions) {
steps.push({
index: steps.length + 1,
...solution
});
}
// If no conclusion yet, create one from remaining constraints
if (!conclusion && steps.length >= 1) {
// Check if we solved for any variables
const lastStep = steps[steps.length - 1];
if (lastStep.rule === 'Equation Solution') {
conclusion = lastStep.statement;
} else {
conclusion = 'Mathematical problem solved';
}
} else if (!conclusion) {
conclusion = 'Unable to find a solution';
}
return {
status: 'success',
message: 'Solution generated successfully.',
details: {
system: 'mathematical',
solution: {
steps,
conclusion
}
}
};
}
/**
* Solve mathematical constraints
* @param constraints Array of constraints
* @returns Array of solution steps
*/
solveConstraints(constraints: MathematicalCondition[]): any[] {
const steps: any[] = [];
for (let i = 0; i < constraints.length; i++) {
const constraint = constraints[i];
// Skip pattern constraints - they're already handled in sequence solving
if (constraint.type === 'pattern') {
continue;
}
const solution = this.solveConstraint(constraint);
if (solution) {
steps.push(solution);
}
}
return steps;
}
/**
* Solve a single constraint
* @param constraint The constraint to solve
* @returns Solution step or null
*/
solveConstraint(constraint: MathematicalCondition): any | null {
switch (constraint.type) {
case 'equals':
// Try to solve for a variable
const solution = this.solveEquation(constraint);
if (solution) {
return solution;
}
// If can't solve, evaluate if possible
try {
const leftVal = this.evaluator.evaluateExpression(constraint.left);
const rightVal = this.evaluator.evaluateExpression(constraint.right);
return {
statement: `${leftVal} = ${rightVal}`,
rule: 'Equality Evaluation',
justification: leftVal === rightVal ? 'Equality holds' : 'Equality does not hold'
};
} catch {
// Can't evaluate, return null
return null;
}
case 'compare':
// Comparison operations
try {
const leftCompareVal = this.evaluator.evaluateExpression(constraint.left);
const rightCompareVal = this.evaluator.evaluateExpression(constraint.right);
const result = this.evaluator.compare(leftCompareVal, constraint.operator, rightCompareVal);
return {
statement: `${leftCompareVal} ${constraint.operator} ${rightCompareVal}`,
rule: 'Comparison Evaluation',
justification: result ? 'Comparison is true' : 'Comparison is false'
};
} catch {
// Can't evaluate, return null
return null;
}
default:
return null;
}
}
/**
* Solve a simple equation for a variable
* @param constraint Equals constraint
* @returns Solution if found
*/
solveEquation(constraint: { type: 'equals'; left: MathematicalTerm; right: MathematicalTerm }): any | null {
// Handle simple cases: x + 5 = 10, 3 + x = 10, x - 5 = 10, etc.
// Check if left side has a variable
const leftVars = this.evaluator.getVariables(constraint.left);
const rightVars = this.evaluator.getVariables(constraint.right);
// For now, only handle equations with one variable
if (leftVars.length === 1 && rightVars.length === 0) {
const variable = leftVars[0];
const value = this.isolateVariable(constraint.left, constraint.right, variable);
if (value !== undefined) {
return {
statement: `${variable} = ${value}`,
rule: 'Equation Solution',
justification: `Solved for ${variable}`
};
}
} else if (leftVars.length === 0 && rightVars.length === 1) {
const variable = rightVars[0];
const value = this.isolateVariable(constraint.right, constraint.left, variable);
if (value !== undefined) {
return {
statement: `${variable} = ${value}`,
rule: 'Equation Solution',
justification: `Solved for ${variable}`
};
}
}
return null;
}
/**
* Isolate a variable in an equation
* @param leftTerm The term containing the variable
* @param rightTerm The other side of the equation
* @param variable The variable to solve for
* @returns Value of the variable if found
*/
isolateVariable(leftTerm: MathematicalTerm, rightTerm: MathematicalTerm, variable: string): number | undefined {
// Handle simple cases
if (leftTerm.type === 'variable' && leftTerm.name === variable) {
// x = rightTerm
try {
return this.evaluator.evaluateExpression(rightTerm);
} catch {
return undefined;
}
}
if (leftTerm.type === 'operation' && leftTerm.operands.length === 2) {
const [op1, op2] = leftTerm.operands;
// x + 5 = 10 => x = 10 - 5
if (leftTerm.operator === '+') {
if (op1.type === 'variable' && op1.name === variable) {
try {
const rightValue = this.evaluator.evaluateExpression(rightTerm);
const op2Value = this.evaluator.evaluateExpression(op2);
return rightValue - op2Value;
} catch {
return undefined;
}
}
if (op2.type === 'variable' && op2.name === variable) {
try {
const rightValue = this.evaluator.evaluateExpression(rightTerm);
const op1Value = this.evaluator.evaluateExpression(op1);
return rightValue - op1Value;
} catch {
return undefined;
}
}
}
// x - 5 = 10 => x = 10 + 5
if (leftTerm.operator === '-') {
if (op1.type === 'variable' && op1.name === variable) {
try {
const rightValue = this.evaluator.evaluateExpression(rightTerm);
const op2Value = this.evaluator.evaluateExpression(op2);
return rightValue + op2Value;
} catch {
return undefined;
}
}
}
// 5 - x = 10 => x = 5 - 10
if (leftTerm.operator === '-' && op2.type === 'variable' && op2.name === variable) {
try {
const rightValue = this.evaluator.evaluateExpression(rightTerm);
const op1Value = this.evaluator.evaluateExpression(op1);
return op1Value - rightValue;
} catch {
return undefined;
}
}
// x * 5 = 10 => x = 10 / 5
if (leftTerm.operator === '*') {
if (op1.type === 'variable' && op1.name === variable) {
try {
const rightValue = this.evaluator.evaluateExpression(rightTerm);
const op2Value = this.evaluator.evaluateExpression(op2);
if (op2Value === 0) return undefined; // Avoid division by zero
return rightValue / op2Value;
} catch {
return undefined;
}
}
if (op2.type === 'variable' && op2.name === variable) {
try {
const rightValue = this.evaluator.evaluateExpression(rightTerm);
const op1Value = this.evaluator.evaluateExpression(op1);
if (op1Value === 0) return undefined; // Avoid division by zero
return rightValue / op1Value;
} catch {
return undefined;
}
}
}
// x / 5 = 10 => x = 10 * 5
if (leftTerm.operator === '/' && op1.type === 'variable' && op1.name === variable) {
try {
const rightValue = this.evaluator.evaluateExpression(rightTerm);
const op2Value = this.evaluator.evaluateExpression(op2);
return rightValue * op2Value;
} catch {
return undefined;
}
}
// 5 / x = 10 => x = 5 / 10
if (leftTerm.operator === '/' && op2.type === 'variable' && op2.name === variable) {
try {
const rightValue = this.evaluator.evaluateExpression(rightTerm);
const op1Value = this.evaluator.evaluateExpression(op1);
if (rightValue === 0) return undefined; // Avoid division by zero
return op1Value / rightValue;
} catch {
return undefined;
}
}
}
return undefined;
}
/**
* Extract sequences from constraints
* @param constraints Array of constraints
* @returns Array of sequences with names and values
*/
private extractSequencesFromConstraints(constraints: MathematicalCondition[]): Array<{name: string, values: number[]}> {
const sequences: Array<{name: string, values: number[]}> = [];
for (let i = 0; i < constraints.length; i++) {
const constraint = constraints[i];
if (constraint.type === 'pattern' && Array.isArray(constraint.sequence)) {
const values = constraint.sequence
.filter(term => term.type === 'number')
.map(term => (term as any).value);
if (values.length > 0) {
sequences.push({
name: `Sequence ${i + 1}`,
values
});
}
}
}
return sequences;
}
/**
* Solve system of linear equations
* @param equations Array of equation constraints
* @returns Solution for variables
*/
solveLinearSystem(equations: Array<{ type: 'equals'; left: MathematicalTerm; right: MathematicalTerm }>): Map<string, number> | null {
// This is a placeholder for more complex equation solving
// In a full implementation, this would use matrix methods or substitution
const solutions = new Map<string, number>();
// Try to solve each equation individually first
for (const equation of equations) {
const solution = this.solveEquation(equation);
if (solution && solution.rule === 'Equation Solution') {
// Extract variable and value from statement like "x = 5"
const match = solution.statement.match(/(\w+)\s*=\s*([-\d.]+)/);
if (match) {
solutions.set(match[1], parseFloat(match[2]));
}
}
}
return solutions.size > 0 ? solutions : null;
}
}