diff --git a/lib/calculator.dart b/lib/calculator.dart index e29e5f9..53c221a 100644 --- a/lib/calculator.dart +++ b/lib/calculator.dart @@ -1,5 +1,5 @@ // === 在 abstract class Expr 中添加声明 === -import 'dart:math' show sqrt, cos, sin, tan; +import 'dart:math' show sqrt, cos, sin, tan, pow; abstract class Expr { Expr simplify(); @@ -488,7 +488,7 @@ class SqrtExpr extends Expr { SqrtExpr(inner.substitute(varName, value)); @override - String toString() => "sqrt($inner)"; + String toString() => "\\sqrt{${inner.toString()}}"; } // === CosExpr === @@ -584,6 +584,90 @@ class TanExpr extends Expr { String toString() => "tan($inner)"; } +// === PowExpr === +class PowExpr extends Expr { + final Expr left, right; + PowExpr(this.left, this.right); + + @override + Expr simplify() { + var l = left.simplify(); + var r = right.simplify(); + + // x^0 = 1 + if (r is IntExpr && r.value == 0) return IntExpr(1); + // x^1 = x + if (r is IntExpr && r.value == 1) return l; + // 1^x = 1 + if (l is IntExpr && l.value == 1) return IntExpr(1); + // 0^x = 0 (for x != 0) + if (l is IntExpr && l.value == 0 && !(r is IntExpr && r.value == 0)) { + return IntExpr(0); + } + + return PowExpr(l, r); + } + + @override + Expr evaluate() { + var l = left.evaluate(); + var r = right.evaluate(); + + // x^0 = 1 + if (r is IntExpr && r.value == 0) return IntExpr(1); + // x^1 = x + if (r is IntExpr && r.value == 1) return l; + // 1^x = 1 + if (l is IntExpr && l.value == 1) return IntExpr(1); + // 0^x = 0 (for x != 0) + if (l is IntExpr && l.value == 0 && !(r is IntExpr && r.value == 0)) + return IntExpr(0); + + // If both are numbers, compute + if (l is IntExpr && r is IntExpr) { + return DoubleExpr(pow(l.value.toDouble(), r.value.toDouble()).toDouble()); + } + if (l is DoubleExpr && r is IntExpr) { + return DoubleExpr(pow(l.value, r.value.toDouble()).toDouble()); + } + if (l is IntExpr && r is DoubleExpr) { + return DoubleExpr(pow(l.value.toDouble(), r.value).toDouble()); + } + if (l is DoubleExpr && r is DoubleExpr) { + return DoubleExpr(pow(l.value, r.value).toDouble()); + } + + return PowExpr(l, r); + } + + @override + Expr substitute(String varName, Expr value) => PowExpr( + left.substitute(varName, value), + right.substitute(varName, value), + ); + + @override + String toString() { + String leftStr = left.toString(); + String rightStr = right.toString(); + + // Remove outer parentheses + if (leftStr.startsWith('(') && leftStr.endsWith(')')) { + leftStr = leftStr.substring(1, leftStr.length - 1); + } + if (rightStr.startsWith('(') && rightStr.endsWith(')')) { + rightStr = rightStr.substring(1, rightStr.length - 1); + } + + // Add parentheses around base if it's a complex expression + bool needsParens = + !(left is VarExpr || left is IntExpr || left is DoubleExpr); + String base = needsParens ? '($leftStr)' : leftStr; + + return '$base^{$rightStr}'; + } +} + // === 辅助:识别 a * sqrt(X) 形式 === class _SqrtTerm { final int coef; diff --git a/lib/parser.dart b/lib/parser.dart index 39555ae..45773c4 100644 --- a/lib/parser.dart +++ b/lib/parser.dart @@ -33,12 +33,12 @@ class Parser { } Expr parseMul() { - var expr = parseAtom(); + var expr = parsePow(); skipSpaces(); while (!isEnd && (current == '*' || current == '/')) { var op = current; eat(); - var right = parseAtom(); + var right = parsePow(); if (op == '*') { expr = MulExpr(expr, right); } else { @@ -49,6 +49,17 @@ class Parser { return expr; } + Expr parsePow() { + var expr = parseAtom(); + skipSpaces(); + if (!isEnd && current == '^') { + eat(); + var right = parsePow(); // right associative + return PowExpr(expr, right); + } + return expr; + } + Expr parseAtom() { skipSpaces(); if (current == '(') { @@ -106,13 +117,20 @@ class Parser { return VarExpr(varName); } - // 解析整数 + // 解析数字 (整数或小数) var buf = ''; - while (!isEnd && RegExp(r'\d').hasMatch(current)) { + bool hasDot = false; + while (!isEnd && + (RegExp(r'\d').hasMatch(current) || (!hasDot && current == '.'))) { + if (current == '.') hasDot = true; buf += current; eat(); } if (buf.isEmpty) throw Exception("无法解析: $current"); - return IntExpr(int.parse(buf)); + if (hasDot) { + return DoubleExpr(double.parse(buf)); + } else { + return IntExpr(int.parse(buf)); + } } } diff --git a/lib/solver.dart b/lib/solver.dart index ed4c16b..534965e 100644 --- a/lib/solver.dart +++ b/lib/solver.dart @@ -52,12 +52,17 @@ class SolverService { /// 1. 求解简单表达式 CalculationResult _solveSimpleExpression(String input) { final steps = []; + // Parse the input to get LaTeX-formatted version + final parser = Parser(input); + final parsedExpr = parser.parse(); + final latexInput = parsedExpr.toString().replaceAll('*', '\\cdot'); + steps.add( CalculationStep( stepNumber: 1, title: '表达式求值', explanation: '这是一个标准的数学表达式,我们将直接计算其结果。', - formula: '\$\$$input\$\$', + formula: '\$\$$latexInput\$\$', ), ); @@ -113,12 +118,17 @@ class SolverService { /// 2. 求解一元一次方程 CalculationResult _solveLinearEquation(String input) { final steps = []; + // Parse the input to get LaTeX-formatted version + final parser = Parser(input); + final parsedExpr = parser.parse(); + final latexInput = parsedExpr.toString().replaceAll('*', '\\cdot'); + steps.add( CalculationStep( stepNumber: 0, title: '原方程', explanation: '这是一元一次方程。', - formula: '\$\$$input\$\$', + formula: '\$\$$latexInput\$\$', ), ); @@ -1262,8 +1272,7 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} /// 格式化原始方程,保持符号形式 String _formatOriginalEquation(String input) { - // Simply return the original equation with proper LaTeX formatting - // This avoids complex parsing issues and preserves the original symbolic form + // Parse the equation and convert to LaTeX String result = input.replaceAll(' ', ''); // 确保方程格式正确 @@ -1271,9 +1280,31 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} result = '$result=0'; } - // Replace sqrt with LaTeX format - result = result.replaceAll('sqrt(', '\\sqrt{'); - result = result.replaceAll(')', '}'); + final parts = result.split('='); + if (parts.length == 2) { + try { + final leftParser = Parser(parts[0]); + final leftExpr = leftParser.parse(); + final rightParser = Parser(parts[1]); + final rightExpr = rightParser.parse(); + result = + '${leftExpr.toString().replaceAll('*', '\\cdot')}=${rightExpr.toString().replaceAll('*', '\\cdot')}'; + } catch (e) { + // Fallback to original if parsing fails + result = result.replaceAll('sqrt(', '\\sqrt{'); + result = result.replaceAll(')', '}'); + } + } else { + try { + final parser = Parser(result.split('=')[0]); + final expr = parser.parse(); + result = '${expr.toString().replaceAll('*', '\\cdot')}=0'; + } catch (e) { + // Fallback + result = result.replaceAll('sqrt(', '\\sqrt{'); + result = result.replaceAll(')', '}'); + } + } return '\$\$$result\$\$'; } diff --git a/test/calculator_test.dart b/test/calculator_test.dart index 421c4b5..555d6b9 100644 --- a/test/calculator_test.dart +++ b/test/calculator_test.dart @@ -39,7 +39,7 @@ void main() { test('非完全平方数', () { var expr = Parser("sqrt(8)").parse(); - expect(expr.simplify().toString().replaceAll(' ', ''), "(2*sqrt(2))"); + expect(expr.simplify().toString().replaceAll(' ', ''), "(2*\\sqrt{2})"); }); }); @@ -53,7 +53,7 @@ void main() { var expr = Parser("sqrt(8)/4 + 1/2").parse(); expect( expr.evaluate().toString().replaceAll(' ', ''), - "((sqrt(2)/2)+1/2)", + "((\\sqrt{2}/2)+1/2)", ); }); });