import {
MathematicalArgument,
MathematicalTerm,
MathematicalCondition,
MathematicalOperator,
InputFormat,
LogicResult
} from '../../types.js';
/**
* Mathematical formatter
* Handles formatting of mathematical expressions for different output formats
*/
export class MathematicalFormatter {
/**
* Format mathematical input for output
* @param argument The mathematical argument
* @param format Output format
* @param variableMap Variable mappings
* @param sequenceMap Sequence mappings
* @returns Formatting result
*/
formatMathematical(
argument: MathematicalArgument,
format: InputFormat,
variableMap: Map<string, string> = new Map(),
sequenceMap: Map<string, number[]> = new Map()
): LogicResult {
let formattedOutput: string;
if (format === 'symbolic') {
// Format in mathematical notation
formattedOutput = this.formatSymbolic(argument);
} else {
// Format in natural language
formattedOutput = this.formatNatural(argument);
}
// Add variable and sequence mappings
if (variableMap.size > 0 || sequenceMap.size > 0) {
formattedOutput += "\n\nMathematical entities identified:\n";
if (variableMap.size > 0) {
formattedOutput += "\nVariables:\n";
for (const [statement, variable] of variableMap.entries()) {
formattedOutput += `• ${variable}: ${statement}\n`;
}
}
if (sequenceMap.size > 0) {
formattedOutput += "\nSequences:\n";
for (const [name, values] of sequenceMap.entries()) {
formattedOutput += `• ${name}: ${values.join(', ')}\n`;
}
}
}
return {
status: 'success',
message: 'Mathematical input formatted successfully.',
details: {
system: 'mathematical',
formalizedInput: formattedOutput
}
};
}
/**
* Format argument in symbolic notation
* @param argument The mathematical argument
* @returns Symbolic representation
*/
formatSymbolic(argument: MathematicalArgument): string {
const termStrings = argument.terms.map(term => this.termToString(term));
const constraintStrings = argument.constraints.map(constraint => this.constraintToString(constraint));
return [
'Terms:',
...termStrings,
'Constraints:',
...constraintStrings,
'Question:',
argument.question
].join('\n');
}
/**
* Format argument in natural language
* @param argument The mathematical argument
* @returns Natural language representation
*/
formatNatural(argument: MathematicalArgument): string {
// Convert to natural language description
const sequences = this.extractSequencesFromConstraints(argument.constraints);
const description: string[] = [];
for (const {name, values} of sequences) {
description.push(`${name}: ${values.join(', ')}`);
}
for (const constraint of argument.constraints) {
if (constraint.type !== 'pattern') {
description.push(this.constraintToNatural(constraint));
}
}
description.push(argument.question);
return description.join('\n');
}
/**
* Convert mathematical term to string
* @param term The term to convert
* @returns String representation
*/
termToString(term: MathematicalTerm): string {
switch (term.type) {
case 'number':
return term.value.toString();
case 'variable':
return term.name;
case 'operation':
const opString = term.operands.map(op => this.termToString(op)).join(` ${term.operator} `);
return `(${opString})`;
case 'sequence':
return `${term.name}[${term.index}]`;
default:
return 'unknown';
}
}
/**
* Convert constraint to string
* @param constraint The constraint to convert
* @returns String representation
*/
constraintToString(constraint: MathematicalCondition): string {
switch (constraint.type) {
case 'equals':
return `${this.termToString(constraint.left)} = ${this.termToString(constraint.right)}`;
case 'compare':
return `${this.termToString(constraint.left)} ${constraint.operator} ${this.termToString(constraint.right)}`;
case 'range':
return `${constraint.min} ≤ ${this.termToString(constraint.value)} ≤ ${constraint.max}`;
case 'pattern':
return `Pattern: sequence of ${constraint.sequence.length} elements`;
default:
return 'unknown constraint';
}
}
/**
* Convert constraint to natural language
* @param constraint The constraint to convert
* @returns Natural language representation
*/
constraintToNatural(constraint: MathematicalCondition): string {
switch (constraint.type) {
case 'equals':
return `${this.termToString(constraint.left)} equals ${this.termToString(constraint.right)}`;
case 'compare':
const opMap: Record<string, string> = {
'<': 'is less than',
'>': 'is greater than',
'<=': 'is less than or equal to',
'>=': 'is greater than or equal to'
};
return `${this.termToString(constraint.left)} ${opMap[constraint.operator] || constraint.operator} ${this.termToString(constraint.right)}`;
case 'range':
return `${this.termToString(constraint.value)} is between ${constraint.min} and ${constraint.max}`;
case 'pattern':
return `The sequence has ${constraint.sequence.length} elements`;
default:
return 'unknown constraint';
}
}
/**
* 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;
}
/**
* Format term for display with proper parentheses
* @param term The term to format
* @param parentOperator Parent operator for precedence
* @returns Formatted string
*/
formatTermWithPrecedence(term: MathematicalTerm, parentOperator?: MathematicalOperator): string {
if (term.type === 'operation') {
const needsParens = parentOperator && this.needsParentheses(term.operator, parentOperator);
const inner = term.operands
.map(op => this.formatTermWithPrecedence(op, term.operator))
.join(` ${term.operator} `);
return needsParens ? `(${inner})` : inner;
}
return this.termToString(term);
}
/**
* Check if parentheses are needed based on operator precedence
* @param childOp Child operator
* @param parentOp Parent operator
* @returns Boolean indicating if parentheses are needed
*/
private needsParentheses(childOp: MathematicalOperator, parentOp: MathematicalOperator): boolean {
const precedence: Record<MathematicalOperator, number> = {
'+': 1,
'-': 1,
'*': 2,
'/': 2,
'%': 2,
'=': 0,
'<': 0,
'>': 0,
'<=': 0,
'>=': 0,
'!=': 0,
'sum': 1,
'product': 2,
'difference': 1,
'ratio': 2,
'power': 3
};
return precedence[childOp] < precedence[parentOp];
}
}