✨ Support calculate ^0.5
This commit is contained in:
		| @@ -1,5 +1,5 @@ | |||||||
| // === 在 abstract class Expr 中添加声明 === | // === 在 abstract class Expr 中添加声明 === | ||||||
| import 'dart:math' show sqrt, cos, sin, tan; | import 'dart:math' show sqrt, cos, sin, tan, pow; | ||||||
|  |  | ||||||
| abstract class Expr { | abstract class Expr { | ||||||
|   Expr simplify(); |   Expr simplify(); | ||||||
| @@ -488,7 +488,7 @@ class SqrtExpr extends Expr { | |||||||
|       SqrtExpr(inner.substitute(varName, value)); |       SqrtExpr(inner.substitute(varName, value)); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   String toString() => "sqrt($inner)"; |   String toString() => "\\sqrt{${inner.toString()}}"; | ||||||
| } | } | ||||||
|  |  | ||||||
| // === CosExpr === | // === CosExpr === | ||||||
| @@ -584,6 +584,90 @@ class TanExpr extends Expr { | |||||||
|   String toString() => "tan($inner)"; |   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) 形式 === | // === 辅助:识别 a * sqrt(X) 形式 === | ||||||
| class _SqrtTerm { | class _SqrtTerm { | ||||||
|   final int coef; |   final int coef; | ||||||
|   | |||||||
| @@ -33,12 +33,12 @@ class Parser { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   Expr parseMul() { |   Expr parseMul() { | ||||||
|     var expr = parseAtom(); |     var expr = parsePow(); | ||||||
|     skipSpaces(); |     skipSpaces(); | ||||||
|     while (!isEnd && (current == '*' || current == '/')) { |     while (!isEnd && (current == '*' || current == '/')) { | ||||||
|       var op = current; |       var op = current; | ||||||
|       eat(); |       eat(); | ||||||
|       var right = parseAtom(); |       var right = parsePow(); | ||||||
|       if (op == '*') { |       if (op == '*') { | ||||||
|         expr = MulExpr(expr, right); |         expr = MulExpr(expr, right); | ||||||
|       } else { |       } else { | ||||||
| @@ -49,6 +49,17 @@ class Parser { | |||||||
|     return expr; |     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() { |   Expr parseAtom() { | ||||||
|     skipSpaces(); |     skipSpaces(); | ||||||
|     if (current == '(') { |     if (current == '(') { | ||||||
| @@ -106,13 +117,20 @@ class Parser { | |||||||
|       return VarExpr(varName); |       return VarExpr(varName); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // 解析整数 |     // 解析数字 (整数或小数) | ||||||
|     var buf = ''; |     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; |       buf += current; | ||||||
|       eat(); |       eat(); | ||||||
|     } |     } | ||||||
|     if (buf.isEmpty) throw Exception("无法解析: $current"); |     if (buf.isEmpty) throw Exception("无法解析: $current"); | ||||||
|     return IntExpr(int.parse(buf)); |     if (hasDot) { | ||||||
|  |       return DoubleExpr(double.parse(buf)); | ||||||
|  |     } else { | ||||||
|  |       return IntExpr(int.parse(buf)); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -52,12 +52,17 @@ class SolverService { | |||||||
|   /// 1. 求解简单表达式 |   /// 1. 求解简单表达式 | ||||||
|   CalculationResult _solveSimpleExpression(String input) { |   CalculationResult _solveSimpleExpression(String input) { | ||||||
|     final steps = <CalculationStep>[]; |     final steps = <CalculationStep>[]; | ||||||
|  |     // Parse the input to get LaTeX-formatted version | ||||||
|  |     final parser = Parser(input); | ||||||
|  |     final parsedExpr = parser.parse(); | ||||||
|  |     final latexInput = parsedExpr.toString().replaceAll('*', '\\cdot'); | ||||||
|  |  | ||||||
|     steps.add( |     steps.add( | ||||||
|       CalculationStep( |       CalculationStep( | ||||||
|         stepNumber: 1, |         stepNumber: 1, | ||||||
|         title: '表达式求值', |         title: '表达式求值', | ||||||
|         explanation: '这是一个标准的数学表达式,我们将直接计算其结果。', |         explanation: '这是一个标准的数学表达式,我们将直接计算其结果。', | ||||||
|         formula: '\$\$$input\$\$', |         formula: '\$\$$latexInput\$\$', | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -113,12 +118,17 @@ class SolverService { | |||||||
|   /// 2. 求解一元一次方程 |   /// 2. 求解一元一次方程 | ||||||
|   CalculationResult _solveLinearEquation(String input) { |   CalculationResult _solveLinearEquation(String input) { | ||||||
|     final steps = <CalculationStep>[]; |     final steps = <CalculationStep>[]; | ||||||
|  |     // Parse the input to get LaTeX-formatted version | ||||||
|  |     final parser = Parser(input); | ||||||
|  |     final parsedExpr = parser.parse(); | ||||||
|  |     final latexInput = parsedExpr.toString().replaceAll('*', '\\cdot'); | ||||||
|  |  | ||||||
|     steps.add( |     steps.add( | ||||||
|       CalculationStep( |       CalculationStep( | ||||||
|         stepNumber: 0, |         stepNumber: 0, | ||||||
|         title: '原方程', |         title: '原方程', | ||||||
|         explanation: '这是一元一次方程。', |         explanation: '这是一元一次方程。', | ||||||
|         formula: '\$\$$input\$\$', |         formula: '\$\$$latexInput\$\$', | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -1262,8 +1272,7 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | |||||||
|  |  | ||||||
|   /// 格式化原始方程,保持符号形式 |   /// 格式化原始方程,保持符号形式 | ||||||
|   String _formatOriginalEquation(String input) { |   String _formatOriginalEquation(String input) { | ||||||
|     // Simply return the original equation with proper LaTeX formatting |     // Parse the equation and convert to LaTeX | ||||||
|     // This avoids complex parsing issues and preserves the original symbolic form |  | ||||||
|     String result = input.replaceAll(' ', ''); |     String result = input.replaceAll(' ', ''); | ||||||
|  |  | ||||||
|     // 确保方程格式正确 |     // 确保方程格式正确 | ||||||
| @@ -1271,9 +1280,31 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | |||||||
|       result = '$result=0'; |       result = '$result=0'; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Replace sqrt with LaTeX format |     final parts = result.split('='); | ||||||
|     result = result.replaceAll('sqrt(', '\\sqrt{'); |     if (parts.length == 2) { | ||||||
|     result = result.replaceAll(')', '}'); |       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\$\$'; |     return '\$\$$result\$\$'; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ void main() { | |||||||
|  |  | ||||||
|     test('非完全平方数', () { |     test('非完全平方数', () { | ||||||
|       var expr = Parser("sqrt(8)").parse(); |       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(); |       var expr = Parser("sqrt(8)/4 + 1/2").parse(); | ||||||
|       expect( |       expect( | ||||||
|         expr.evaluate().toString().replaceAll(' ', ''), |         expr.evaluate().toString().replaceAll(' ', ''), | ||||||
|         "((sqrt(2)/2)+1/2)", |         "((\\sqrt{2}/2)+1/2)", | ||||||
|       ); |       ); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user