✨ Support calculate ^0.5
This commit is contained in:
		| @@ -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; | ||||
|   | ||||
| @@ -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"); | ||||
|     if (hasDot) { | ||||
|       return DoubleExpr(double.parse(buf)); | ||||
|     } else { | ||||
|       return IntExpr(int.parse(buf)); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -52,12 +52,17 @@ class SolverService { | ||||
|   /// 1. 求解简单表达式 | ||||
|   CalculationResult _solveSimpleExpression(String input) { | ||||
|     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( | ||||
|       CalculationStep( | ||||
|         stepNumber: 1, | ||||
|         title: '表达式求值', | ||||
|         explanation: '这是一个标准的数学表达式,我们将直接计算其结果。', | ||||
|         formula: '\$\$$input\$\$', | ||||
|         formula: '\$\$$latexInput\$\$', | ||||
|       ), | ||||
|     ); | ||||
|  | ||||
| @@ -113,12 +118,17 @@ class SolverService { | ||||
|   /// 2. 求解一元一次方程 | ||||
|   CalculationResult _solveLinearEquation(String input) { | ||||
|     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( | ||||
|       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 | ||||
|     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\$\$'; | ||||
|   } | ||||
|   | ||||
| @@ -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)", | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user