💄 Improve accurate of sqrt calculation
This commit is contained in:
		
							
								
								
									
										463
									
								
								lib/solver.dart
									
									
									
									
									
								
							
							
						
						
									
										463
									
								
								lib/solver.dart
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| import 'dart:math'; | import 'dart:math'; | ||||||
| import 'package:flutter/foundation.dart'; // For kDebugMode | import 'package:flutter/foundation.dart'; // For kDebugMode | ||||||
| import 'package:math_expressions/math_expressions.dart'; | import 'package:math_expressions/math_expressions.dart'; | ||||||
|  | import 'package:rational/rational.dart'; | ||||||
| import 'models/calculation_step.dart'; | import 'models/calculation_step.dart'; | ||||||
|  |  | ||||||
| /// 帮助解析一元一次方程 ax+b=cx+d 的辅助类 | /// 帮助解析一元一次方程 ax+b=cx+d 的辅助类 | ||||||
| @@ -103,8 +104,8 @@ class SolverService { | |||||||
|     final parts = _parseLinearEquation(input); |     final parts = _parseLinearEquation(input); | ||||||
|     final a = parts.a, b = parts.b, c = parts.c, d = parts.d; |     final a = parts.a, b = parts.b, c = parts.c, d = parts.d; | ||||||
|  |  | ||||||
|     final newA = a - c; |     final newA = _rationalFromDouble(a) - _rationalFromDouble(c); | ||||||
|     final newD = d - b; |     final newD = _rationalFromDouble(d) - _rationalFromDouble(b); | ||||||
|  |  | ||||||
|     steps.add( |     steps.add( | ||||||
|       CalculationStep( |       CalculationStep( | ||||||
| @@ -121,14 +122,15 @@ class SolverService { | |||||||
|         stepNumber: 2, |         stepNumber: 2, | ||||||
|         title: '合并同类项', |         title: '合并同类项', | ||||||
|         explanation: '合并等式两边的项。', |         explanation: '合并等式两边的项。', | ||||||
|         formula: '\$\$${newA}x = $newD\$\$', |         formula: | ||||||
|  |             '\$\$${newA.toDouble().toStringAsFixed(4)}x = ${newD.toDouble().toStringAsFixed(4)}\$\$', | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     if (newA == 0) { |     if (newA == Rational.zero) { | ||||||
|       return CalculationResult( |       return CalculationResult( | ||||||
|         steps: steps, |         steps: steps, | ||||||
|         finalAnswer: newD == 0 ? '有无穷多解' : '无解', |         finalAnswer: newD == Rational.zero ? '有无穷多解' : '无解', | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -152,9 +154,29 @@ class SolverService { | |||||||
|     final eqParts = input.split('='); |     final eqParts = input.split('='); | ||||||
|     if (eqParts.length != 2) throw Exception("方程格式错误,应包含一个 '='。"); |     if (eqParts.length != 2) throw Exception("方程格式错误,应包含一个 '='。"); | ||||||
|  |  | ||||||
|  |     // Keep original equation for display | ||||||
|  |     final originalEquation = _formatOriginalEquation(input); | ||||||
|  |  | ||||||
|  |     // Parse coefficients symbolically | ||||||
|  |     final leftCoeffsSymbolic = _parsePolynomialSymbolic(eqParts[0]); | ||||||
|  |     final rightCoeffsSymbolic = _parsePolynomialSymbolic(eqParts[1]); | ||||||
|  |  | ||||||
|  |     final aSymbolic = _subtractCoefficients( | ||||||
|  |       leftCoeffsSymbolic[2] ?? '0', | ||||||
|  |       rightCoeffsSymbolic[2] ?? '0', | ||||||
|  |     ); | ||||||
|  |     final bSymbolic = _subtractCoefficients( | ||||||
|  |       leftCoeffsSymbolic[1] ?? '0', | ||||||
|  |       rightCoeffsSymbolic[1] ?? '0', | ||||||
|  |     ); | ||||||
|  |     final cSymbolic = _subtractCoefficients( | ||||||
|  |       leftCoeffsSymbolic[0] ?? '0', | ||||||
|  |       rightCoeffsSymbolic[0] ?? '0', | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     // Also get numeric values for calculations | ||||||
|     final leftCoeffs = _parsePolynomial(eqParts[0]); |     final leftCoeffs = _parsePolynomial(eqParts[0]); | ||||||
|     final rightCoeffs = _parsePolynomial(eqParts[1]); |     final rightCoeffs = _parsePolynomial(eqParts[1]); | ||||||
|  |  | ||||||
|     final a = (leftCoeffs[2] ?? 0) - (rightCoeffs[2] ?? 0); |     final a = (leftCoeffs[2] ?? 0) - (rightCoeffs[2] ?? 0); | ||||||
|     final b = (leftCoeffs[1] ?? 0) - (rightCoeffs[1] ?? 0); |     final b = (leftCoeffs[1] ?? 0) - (rightCoeffs[1] ?? 0); | ||||||
|     final c = (leftCoeffs[0] ?? 0) - (rightCoeffs[0] ?? 0); |     final c = (leftCoeffs[0] ?? 0) - (rightCoeffs[0] ?? 0); | ||||||
| @@ -168,8 +190,7 @@ class SolverService { | |||||||
|         stepNumber: 1, |         stepNumber: 1, | ||||||
|         title: '整理方程', |         title: '整理方程', | ||||||
|         explanation: r'将方程整理成标准形式 $ax^2+bx+c=0$。', |         explanation: r'将方程整理成标准形式 $ax^2+bx+c=0$。', | ||||||
|         formula: |         formula: originalEquation, | ||||||
|             '\$\$${a}x^2 ${b >= 0 ? '+' : ''} ${b}x ${c >= 0 ? '+' : ''} $c = 0\$\$', |  | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -213,36 +234,47 @@ class SolverService { | |||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     final delta = b * b - 4 * a * c; |     // Calculate delta symbolically | ||||||
|  |     final deltaSymbolic = _calculateDeltaSymbolic( | ||||||
|  |       aSymbolic, | ||||||
|  |       bSymbolic, | ||||||
|  |       cSymbolic, | ||||||
|  |     ); | ||||||
|  |     final delta = | ||||||
|  |         _rationalFromDouble(b).pow(2) - | ||||||
|  |         Rational.fromInt(4) * _rationalFromDouble(a) * _rationalFromDouble(c); | ||||||
|  |  | ||||||
|     steps.add( |     steps.add( | ||||||
|       CalculationStep( |       CalculationStep( | ||||||
|         stepNumber: 3, |         stepNumber: 3, | ||||||
|         title: '计算判别式 (Delta)', |         title: '计算判别式 (Delta)', | ||||||
|         explanation: |         explanation: '\$\$\\Delta = b^2 - 4ac = $deltaSymbolic\$\$', | ||||||
|             '\$\$\\Delta = b^2 - 4ac = ($b)^2 - 4 \\cdot ($a) \\cdot ($c) = $delta\$\$', |         formula: | ||||||
|         formula: '\$\$\\Delta = $delta\$\$', |             '\$\$\\Delta = $deltaSymbolic = ${delta.toDouble().toStringAsFixed(4)}\$\$', | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     if (delta > 0) { |     final deltaDouble = delta.toDouble(); | ||||||
|       final x1 = (-b + sqrt(delta)) / (2 * a); |     if (deltaDouble > 0) { | ||||||
|       final x2 = (-b - sqrt(delta)) / (2 * a); |       // Keep sqrt symbolic instead of evaluating to decimal | ||||||
|  |       final sqrtDeltaStr = _formatSqrtExpression(delta.toDouble()); | ||||||
|  |       final x1Expr = _formatQuadraticRoot(-b, sqrtDeltaStr, 2 * a, true); | ||||||
|  |       final x2Expr = _formatQuadraticRoot(-b, sqrtDeltaStr, 2 * a, false); | ||||||
|  |  | ||||||
|       steps.add( |       steps.add( | ||||||
|         CalculationStep( |         CalculationStep( | ||||||
|           stepNumber: 4, |           stepNumber: 4, | ||||||
|           title: '应用求根公式', |           title: '应用求根公式', | ||||||
|           explanation: |           explanation: | ||||||
|               r'因为 $\Delta > 0$,方程有两个不相等的实数根。公式: $x = \frac{-b \pm \sqrt{\Delta}}{2a}$。', |               r'因为 $\Delta > 0$,方程有两个不相等的实数根。公式: $x = \frac{-b \pm \sqrt{\Delta}}{2a}$。', | ||||||
|           formula: |           formula: '\$\$x_1 = $x1Expr, \\quad x_2 = $x2Expr\$\$', | ||||||
|               '\$\$x_1 = ${x1.toStringAsFixed(4)}, \\quad x_2 = ${x2.toStringAsFixed(4)}\$\$', |  | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|       return CalculationResult( |       return CalculationResult( | ||||||
|         steps: steps, |         steps: steps, | ||||||
|         finalAnswer: |         finalAnswer: '\$\$x_1 = $x1Expr, \\quad x_2 = $x2Expr\$\$', | ||||||
|             '\$\$x_1 = ${x1.toStringAsFixed(4)}, \\quad x_2 = ${x2.toStringAsFixed(4)}\$\$', |  | ||||||
|       ); |       ); | ||||||
|     } else if (delta == 0) { |     } else if (deltaDouble == 0) { | ||||||
|       final x = -b / (2 * a); |       final x = -b / (2 * a); | ||||||
|       steps.add( |       steps.add( | ||||||
|         CalculationStep( |         CalculationStep( | ||||||
| @@ -266,9 +298,10 @@ class SolverService { | |||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|       final sqrtDelta = sqrt(-delta); |       // Keep sqrt symbolic for complex roots | ||||||
|  |       final sqrtNegDeltaStr = _formatSqrtExpression(-delta.toDouble()); | ||||||
|       final realPart = -b / (2 * a); |       final realPart = -b / (2 * a); | ||||||
|       final imagPart = sqrtDelta / (2 * a); |       final imagPartExpr = _formatImaginaryPart(sqrtNegDeltaStr, 2 * a); | ||||||
|  |  | ||||||
|       steps.add( |       steps.add( | ||||||
|         CalculationStep( |         CalculationStep( | ||||||
| @@ -282,7 +315,7 @@ class SolverService { | |||||||
|       return CalculationResult( |       return CalculationResult( | ||||||
|         steps: steps, |         steps: steps, | ||||||
|         finalAnswer: |         finalAnswer: | ||||||
|             '\$\$x_1 = ${realPart.toStringAsFixed(4)} + ${imagPart.toStringAsFixed(4)}i, \\quad x_2 = ${realPart.toStringAsFixed(4)} - ${imagPart.toStringAsFixed(4)}i\$\$', |             '\$\$x_1 = ${realPart.toStringAsFixed(4)} + $imagPartExpr, \\quad x_2 = ${realPart.toStringAsFixed(4)} - $imagPartExpr\$\$', | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -316,16 +349,23 @@ ${a2}x ${b2 >= 0 ? '+' : ''} ${b2}y = $c2 & (2) | |||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     final det = a1 * b2 - a2 * b1; |     final det = | ||||||
|     if (det == 0) { |         _rationalFromDouble(a1) * _rationalFromDouble(b2) - | ||||||
|  |         _rationalFromDouble(a2) * _rationalFromDouble(b1); | ||||||
|  |     if (det == Rational.zero) { | ||||||
|  |       final infiniteCheck = | ||||||
|  |           _rationalFromDouble(a1) * _rationalFromDouble(c2) - | ||||||
|  |           _rationalFromDouble(a2) * _rationalFromDouble(c1); | ||||||
|       return CalculationResult( |       return CalculationResult( | ||||||
|         steps: steps, |         steps: steps, | ||||||
|         finalAnswer: a1 * c2 - a2 * c1 == 0 ? '有无穷多解' : '无解', |         finalAnswer: infiniteCheck == Rational.zero ? '有无穷多解' : '无解', | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     final newA1 = a1 * b2, newC1 = c1 * b2; |     final newA1 = _rationalFromDouble(a1) * _rationalFromDouble(b2); | ||||||
|     final newA2 = a2 * b1, newC2 = c2 * b1; |     final newC1 = _rationalFromDouble(c1) * _rationalFromDouble(b2); | ||||||
|  |     final newA2 = _rationalFromDouble(a2) * _rationalFromDouble(b1); | ||||||
|  |     final newC2 = _rationalFromDouble(c2) * _rationalFromDouble(b1); | ||||||
|  |  | ||||||
|     steps.add( |     steps.add( | ||||||
|       CalculationStep( |       CalculationStep( | ||||||
| @@ -336,8 +376,8 @@ ${a2}x ${b2 >= 0 ? '+' : ''} ${b2}y = $c2 & (2) | |||||||
|             ''' |             ''' | ||||||
| \$\$ | \$\$ | ||||||
| \\begin{cases} | \\begin{cases} | ||||||
| ${newA1}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC1 & (3) \\\\ | ${newA1.toDouble().toStringAsFixed(2)}x ${b1 * b2 >= 0 ? '+' : ''} ${(b1 * b2).toStringAsFixed(2)}y = ${newC1.toDouble().toStringAsFixed(2)} & (3) \\\\ | ||||||
| ${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4) | ${newA2.toDouble().toStringAsFixed(2)}x ${b1 * b2 >= 0 ? '+' : ''} ${(b1 * b2).toStringAsFixed(2)}y = ${newC2.toDouble().toStringAsFixed(2)} & (4) | ||||||
| \\end{cases} | \\end{cases} | ||||||
| \$\$ | \$\$ | ||||||
| ''', | ''', | ||||||
| @@ -353,7 +393,7 @@ ${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4) | |||||||
|         title: '相减', |         title: '相减', | ||||||
|         explanation: '将方程(3)减去方程(4),得到一个只含 x 的方程。', |         explanation: '将方程(3)减去方程(4),得到一个只含 x 的方程。', | ||||||
|         formula: |         formula: | ||||||
|             '\$\$($newA1 - $newA2)x = $newC1 - $newC2 \\Rightarrow ${xCoeff}x = $constCoeff\$\$', |             '\$\$(${newA1.toDouble().toStringAsFixed(2)} - ${newA2.toDouble().toStringAsFixed(2)})x = ${newC1.toDouble().toStringAsFixed(2)} - ${newC2.toDouble().toStringAsFixed(2)} \\Rightarrow ${xCoeff.toDouble().toStringAsFixed(2)}x = ${constCoeff.toDouble().toStringAsFixed(2)}\$\$', | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -369,21 +409,21 @@ ${newA2}x ${b1 * b2 >= 0 ? '+' : ''} ${b1 * b2}y = $newC2 & (4) | |||||||
|  |  | ||||||
|     if (b1.abs() < 1e-9) { |     if (b1.abs() < 1e-9) { | ||||||
|       final yCoeff = b2; |       final yCoeff = b2; | ||||||
|       final yConst = c2 - a2 * x; |       final yConst = c2 - a2 * x.toDouble(); | ||||||
|       final y = yConst / yCoeff; |       final y = yConst / yCoeff; | ||||||
|       steps.add( |       steps.add( | ||||||
|         CalculationStep( |         CalculationStep( | ||||||
|           stepNumber: 4, |           stepNumber: 4, | ||||||
|           title: '回代求解 y', |           title: '回代求解 y', | ||||||
|           explanation: '将 x = $x 代入原方程(2)中。', |           explanation: '将 x = ${x.toDouble().toStringAsFixed(4)} 代入原方程(2)中。', | ||||||
|           formula: |           formula: | ||||||
|               ''' |               ''' | ||||||
| \$\$ | \$\$ | ||||||
| \\begin{aligned} | \\begin{aligned} | ||||||
| $a2($x) + ${b2}y &= $c2 \\\\ | $a2(${x.toDouble().toStringAsFixed(4)}) + ${b2}y &= $c2 \\\\ | ||||||
| ${a2 * x} + ${b2}y &= $c2 \\\\ | ${a2 * x.toDouble()} + ${b2}y &= $c2 \\\\ | ||||||
| ${b2}y &= $c2 - ${a2 * x} \\\\ | ${b2}y &= $c2 - ${a2 * x.toDouble()} \\\\ | ||||||
| ${b2}y &= ${c2 - a2 * x} | ${b2}y &= ${c2 - a2 * x.toDouble()} | ||||||
| \\end{aligned} | \\end{aligned} | ||||||
| \$\$ | \$\$ | ||||||
| ''', | ''', | ||||||
| @@ -394,30 +434,31 @@ ${b2}y &= ${c2 - a2 * x} | |||||||
|           stepNumber: 5, |           stepNumber: 5, | ||||||
|           title: '解出 y', |           title: '解出 y', | ||||||
|           explanation: '求解得到 y 的值。', |           explanation: '求解得到 y 的值。', | ||||||
|           formula: '\$\$y = $y\$\$', |           formula: '\$\$y = ${y.toStringAsFixed(4)}\$\$', | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|       return CalculationResult( |       return CalculationResult( | ||||||
|         steps: steps, |         steps: steps, | ||||||
|         finalAnswer: '\$\$x = $x, \\quad y = $y\$\$', |         finalAnswer: | ||||||
|  |             '\$\$x = ${x.toDouble().toStringAsFixed(4)}, \\quad y = ${y.toStringAsFixed(4)}\$\$', | ||||||
|       ); |       ); | ||||||
|     } else { |     } else { | ||||||
|       final yCoeff = b1; |       final yCoeff = b1; | ||||||
|       final yConst = c1 - a1 * x; |       final yConst = c1 - a1 * x.toDouble(); | ||||||
|       final y = yConst / yCoeff; |       final y = yConst / yCoeff; | ||||||
|       steps.add( |       steps.add( | ||||||
|         CalculationStep( |         CalculationStep( | ||||||
|           stepNumber: 4, |           stepNumber: 4, | ||||||
|           title: '回代求解 y', |           title: '回代求解 y', | ||||||
|           explanation: '将 x = $x 代入原方程(1)中。', |           explanation: '将 x = ${x.toDouble().toStringAsFixed(4)} 代入原方程(1)中。', | ||||||
|           formula: |           formula: | ||||||
|               ''' |               ''' | ||||||
| \$\$ | \$\$ | ||||||
| \\begin{aligned} | \\begin{aligned} | ||||||
| $a1($x) + ${b1}y &= $c1 \\\\ | $a1(${x.toDouble().toStringAsFixed(4)}) + ${b1}y &= $c1 \\\\ | ||||||
| ${a1 * x} + ${b1}y &= $c1 \\\\ | ${a1 * x.toDouble()} + ${b1}y &= $c1 \\\\ | ||||||
| ${b1}y &= $c1 - ${a1 * x} \\\\ | ${b1}y &= $c1 - ${a1 * x.toDouble()} \\\\ | ||||||
| ${b1}y &= ${c1 - a1 * x} | ${b1}y &= ${c1 - a1 * x.toDouble()} | ||||||
| \\end{aligned} | \\end{aligned} | ||||||
| \$\$ | \$\$ | ||||||
| ''', | ''', | ||||||
| @@ -428,12 +469,13 @@ ${b1}y &= ${c1 - a1 * x} | |||||||
|           stepNumber: 5, |           stepNumber: 5, | ||||||
|           title: '解出 y', |           title: '解出 y', | ||||||
|           explanation: '求解得到 y 的值。', |           explanation: '求解得到 y 的值。', | ||||||
|           formula: '\$\$y = $y\$\$', |           formula: '\$\$y = ${y.toStringAsFixed(4)}\$\$', | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|       return CalculationResult( |       return CalculationResult( | ||||||
|         steps: steps, |         steps: steps, | ||||||
|         finalAnswer: '\$\$x = $x, \\quad y = $y\$\$', |         finalAnswer: | ||||||
|  |             '\$\$x = ${x.toDouble().toStringAsFixed(4)}, \\quad y = ${y.toStringAsFixed(4)}\$\$', | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -940,4 +982,331 @@ ${b1}y &= ${c1 - a1 * x} | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   int gcd(int a, int b) => b == 0 ? a : gcd(b, a % b); |   int gcd(int a, int b) => b == 0 ? a : gcd(b, a % b); | ||||||
|  |  | ||||||
|  |   /// 格式化平方根表达式,保持符号形式 | ||||||
|  |   String _formatSqrtExpression(double value) { | ||||||
|  |     if (value == 0) return '0'; | ||||||
|  |  | ||||||
|  |     // 处理负数(用于复数根) | ||||||
|  |     if (value < 0) { | ||||||
|  |       return '\\sqrt{${(-value).toInt()}}'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 检查是否为完全平方数 | ||||||
|  |     final sqrtValue = sqrt(value); | ||||||
|  |     final rounded = sqrtValue.round(); | ||||||
|  |     if ((sqrtValue - rounded).abs() < 1e-10) { | ||||||
|  |       return rounded.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 寻找最大的完全平方数因子 | ||||||
|  |     int maxSquareFactor = 1; | ||||||
|  |     int intValue = value.toInt(); | ||||||
|  |     for (int i = 2; i * i <= intValue; i++) { | ||||||
|  |       if (intValue % (i * i) == 0) { | ||||||
|  |         maxSquareFactor = i * i; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     final coefficient = sqrt(maxSquareFactor).round(); | ||||||
|  |     final remaining = intValue ~/ maxSquareFactor; | ||||||
|  |  | ||||||
|  |     if (remaining == 1) { | ||||||
|  |       return coefficient == 1 | ||||||
|  |           ? '\\sqrt{$intValue}' | ||||||
|  |           : '$coefficient\\sqrt{$remaining}'; | ||||||
|  |     } else if (coefficient == 1) { | ||||||
|  |       return '\\sqrt{$remaining}'; | ||||||
|  |     } else { | ||||||
|  |       return '$coefficient\\sqrt{$remaining}'; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// 格式化二次方程的根:(-b ± sqrt(delta)) / (2a) | ||||||
|  |   String _formatQuadraticRoot( | ||||||
|  |     double b, | ||||||
|  |     String sqrtExpr, | ||||||
|  |     double denominator, | ||||||
|  |     bool isPlus, | ||||||
|  |   ) { | ||||||
|  |     final sign = isPlus ? '+' : '-'; | ||||||
|  |     final bStr = b == 0 | ||||||
|  |         ? '' | ||||||
|  |         : b > 0 | ||||||
|  |         ? '${b.toInt()}' | ||||||
|  |         : '(${b.toInt()})'; | ||||||
|  |     final denomStr = denominator == 2 ? '2' : denominator.toString(); | ||||||
|  |  | ||||||
|  |     if (b == 0) { | ||||||
|  |       // 简化为 ±sqrt(delta)/denominator | ||||||
|  |       if (denominator == 2) { | ||||||
|  |         return isPlus | ||||||
|  |             ? '\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{2}' | ||||||
|  |             : '-\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{2}'; | ||||||
|  |       } else { | ||||||
|  |         return isPlus | ||||||
|  |             ? '\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{$denomStr}' | ||||||
|  |             : '-\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{$denomStr}'; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       // 完整的表达式:(-b ± sqrt(delta))/denominator | ||||||
|  |       final numerator = b > 0 | ||||||
|  |           ? '-$bStr $sign \\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}' | ||||||
|  |           : '(${b.toInt()}) $sign \\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}'; | ||||||
|  |  | ||||||
|  |       if (denominator == 2) { | ||||||
|  |         return '\\frac{$numerator}{2}'; | ||||||
|  |       } else { | ||||||
|  |         return '\\frac{$numerator}{$denomStr}'; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// 格式化复数根的虚部:sqrt(-delta)/(2a) | ||||||
|  |   String _formatImaginaryPart(String sqrtExpr, double denominator) { | ||||||
|  |     final denomStr = denominator == 2 ? '2' : denominator.toString(); | ||||||
|  |  | ||||||
|  |     if (denominator == 2) { | ||||||
|  |       return '\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{2}i'; | ||||||
|  |     } else { | ||||||
|  |       return '\\frac{\\sqrt{${sqrtExpr.replaceAll('\\sqrt{', '').replaceAll('}', '')}}}{$denomStr}i'; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// 格式化原始方程,保持符号形式 | ||||||
|  |   String _formatOriginalEquation(String input) { | ||||||
|  |     // Simply return the original equation with proper LaTeX formatting | ||||||
|  |     // This avoids complex parsing issues and preserves the original symbolic form | ||||||
|  |     String result = input.replaceAll(' ', ''); | ||||||
|  |  | ||||||
|  |     // 确保方程格式正确 | ||||||
|  |     if (!result.contains('=')) { | ||||||
|  |       result = '$result=0'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Replace sqrt with LaTeX format | ||||||
|  |     result = result.replaceAll('sqrt(', '\\sqrt{'); | ||||||
|  |     result = result.replaceAll(')', '}'); | ||||||
|  |  | ||||||
|  |     return '\$\$$result\$\$'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// 解析多项式,保持符号形式 | ||||||
|  |   Map<int, String> _parsePolynomialSymbolic(String side) { | ||||||
|  |     final coeffs = <int, String>{}; | ||||||
|  |  | ||||||
|  |     // Use a simpler approach: split by terms and parse each term individually | ||||||
|  |     var s = side.replaceAll(' ', ''); // Remove spaces | ||||||
|  |     if (!s.startsWith('+') && !s.startsWith('-')) { | ||||||
|  |       s = '+$s'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Split by + and - but be more careful about parentheses and functions | ||||||
|  |     final terms = <String>[]; | ||||||
|  |     int start = 0; | ||||||
|  |     int parenDepth = 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < s.length; i++) { | ||||||
|  |       final char = s[i]; | ||||||
|  |  | ||||||
|  |       if (char == '(') | ||||||
|  |         parenDepth++; | ||||||
|  |       else if (char == ')') | ||||||
|  |         parenDepth--; | ||||||
|  |  | ||||||
|  |       // Only split on + or - when not inside parentheses | ||||||
|  |       if (parenDepth == 0 && (char == '+' || char == '-') && i > start) { | ||||||
|  |         terms.add(s.substring(start, i)); | ||||||
|  |         start = i; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     terms.add(s.substring(start)); | ||||||
|  |  | ||||||
|  |     for (final term in terms) { | ||||||
|  |       if (term.isEmpty) continue; | ||||||
|  |  | ||||||
|  |       // Parse each term | ||||||
|  |       final termPattern = RegExp(r'^([+-]?)(.*?)x(?:\^(\d+))?$|^([+-]?)(.*?)$'); | ||||||
|  |       final match = termPattern.firstMatch(term); | ||||||
|  |  | ||||||
|  |       if (match != null) { | ||||||
|  |         if (match.group(5) != null) { | ||||||
|  |           // Constant term | ||||||
|  |           final sign = match.group(4) ?? '+'; | ||||||
|  |           final value = match.group(5)!; | ||||||
|  |           final coeffStr = sign == '+' && value.isNotEmpty | ||||||
|  |               ? value | ||||||
|  |               : '$sign$value'; | ||||||
|  |           coeffs[0] = _combineCoefficients(coeffs[0], coeffStr); | ||||||
|  |         } else { | ||||||
|  |           // x term | ||||||
|  |           final sign = match.group(1) ?? '+'; | ||||||
|  |           final coeffPart = match.group(2) ?? ''; | ||||||
|  |           final power = match.group(3) != null ? int.parse(match.group(3)!) : 1; | ||||||
|  |  | ||||||
|  |           String coeffStr; | ||||||
|  |           if (coeffPart.isEmpty) { | ||||||
|  |             coeffStr = sign == '+' ? '1' : '-1'; | ||||||
|  |           } else { | ||||||
|  |             coeffStr = sign == '+' ? coeffPart : '$sign$coeffPart'; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           coeffs[power] = _combineCoefficients(coeffs[power], coeffStr); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return coeffs; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// 规范化系数字符串 | ||||||
|  |   String _normalizeCoefficientString(String coeff) { | ||||||
|  |     if (coeff.isEmpty || coeff == '+') return '1'; | ||||||
|  |     if (coeff == '-') return '-1'; | ||||||
|  |  | ||||||
|  |     // 处理类似 "2sqrt(3)" 的情况 | ||||||
|  |     coeff = coeff.replaceAll(' ', ''); // 移除空格 | ||||||
|  |  | ||||||
|  |     // 检查是否是纯数字 | ||||||
|  |     final numValue = double.tryParse(coeff); | ||||||
|  |     if (numValue != null) { | ||||||
|  |       return coeff; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 检查是否包含 sqrt | ||||||
|  |     if (coeff.contains('sqrt(') || coeff.contains('\\sqrt{')) { | ||||||
|  |       // 如果前面没有数字系数,默认为 1 | ||||||
|  |       if (coeff.startsWith('sqrt(') || coeff.startsWith('\\sqrt{')) { | ||||||
|  |         return coeff.startsWith('-') ? coeff : '1' + coeff; | ||||||
|  |       } | ||||||
|  |       // 如果前面有数字,保持原样 | ||||||
|  |       return coeff; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return coeff; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// 合并系数,保持符号形式 | ||||||
|  |   String _combineCoefficients(String? existing, String newCoeff) { | ||||||
|  |     if (existing == null || existing == '0') return newCoeff; | ||||||
|  |     if (newCoeff == '0') return existing; | ||||||
|  |  | ||||||
|  |     // 简化逻辑:如果都是数字,可以相加;否则保持原样 | ||||||
|  |     final existingNum = double.tryParse(existing); | ||||||
|  |     final newNum = double.tryParse(newCoeff); | ||||||
|  |  | ||||||
|  |     if (existingNum != null && newNum != null) { | ||||||
|  |       final sum = existingNum + newNum; | ||||||
|  |       return sum.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 如果包含符号表达式,直接连接 | ||||||
|  |     return '$existing+$newCoeff'.replaceAll('+-', '-'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// 减去系数 | ||||||
|  |   String _subtractCoefficients(String a, String b) { | ||||||
|  |     if (a == '0') return b.startsWith('-') ? b.substring(1) : '-$b'; | ||||||
|  |     if (b == '0') return a; | ||||||
|  |  | ||||||
|  |     final aNum = double.tryParse(a); | ||||||
|  |     final bNum = double.tryParse(b); | ||||||
|  |  | ||||||
|  |     if (aNum != null && bNum != null) { | ||||||
|  |       final result = aNum - bNum; | ||||||
|  |       return result.toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 符号表达式相减 | ||||||
|  |     return '$a-${b.startsWith('-') ? b.substring(1) : b}'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// 计算判别式,保持符号形式 | ||||||
|  |   String _calculateDeltaSymbolic(String a, String b, String c) { | ||||||
|  |     // Delta = b^2 - 4ac | ||||||
|  |  | ||||||
|  |     // 计算 b^2 | ||||||
|  |     String bSquared; | ||||||
|  |     if (b == '0') { | ||||||
|  |       bSquared = '0'; | ||||||
|  |     } else if (b == '1') { | ||||||
|  |       bSquared = '1'; | ||||||
|  |     } else if (b == '-1') { | ||||||
|  |       bSquared = '1'; | ||||||
|  |     } else if (b.startsWith('-')) { | ||||||
|  |       final absB = b.substring(1); | ||||||
|  |       bSquared = '$absB^2'; | ||||||
|  |     } else { | ||||||
|  |       bSquared = '$b^2'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 计算 4ac | ||||||
|  |     String fourAC; | ||||||
|  |     if (a == '0' || c == '0') { | ||||||
|  |       fourAC = '0'; | ||||||
|  |     } else { | ||||||
|  |       // 处理符号 | ||||||
|  |       String aCoeff = a; | ||||||
|  |       String cCoeff = c; | ||||||
|  |  | ||||||
|  |       // 如果 a 或 c 是负数,需要处理符号 | ||||||
|  |       bool aNegative = a.startsWith('-'); | ||||||
|  |       bool cNegative = c.startsWith('-'); | ||||||
|  |  | ||||||
|  |       if (aNegative) aCoeff = a.substring(1); | ||||||
|  |       if (cNegative) cCoeff = c.substring(1); | ||||||
|  |  | ||||||
|  |       String acProduct; | ||||||
|  |       if (aCoeff == '1' && cCoeff == '1') { | ||||||
|  |         acProduct = '1'; | ||||||
|  |       } else if (aCoeff == '1') { | ||||||
|  |         acProduct = cCoeff; | ||||||
|  |       } else if (cCoeff == '1') { | ||||||
|  |         acProduct = aCoeff; | ||||||
|  |       } else { | ||||||
|  |         acProduct = '$aCoeff \\cdot $cCoeff'; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // 确定 4ac 的符号 | ||||||
|  |       bool productNegative = aNegative != cNegative; | ||||||
|  |       String fourACValue = '4 \\cdot $acProduct'; | ||||||
|  |  | ||||||
|  |       if (productNegative) { | ||||||
|  |         fourAC = '-$fourACValue'; | ||||||
|  |       } else { | ||||||
|  |         fourAC = fourACValue; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // 计算 Delta = b^2 - 4ac | ||||||
|  |     if (bSquared == '0' && fourAC == '0') { | ||||||
|  |       return '0'; | ||||||
|  |     } else if (bSquared == '0') { | ||||||
|  |       return fourAC.startsWith('-') ? fourAC.substring(1) : '-$fourAC'; | ||||||
|  |     } else if (fourAC == '0') { | ||||||
|  |       return bSquared; | ||||||
|  |     } else { | ||||||
|  |       String sign = fourAC.startsWith('-') ? '+' : '-'; | ||||||
|  |       String absFourAC = fourAC.startsWith('-') ? fourAC.substring(1) : fourAC; | ||||||
|  |       return '$bSquared $sign $absFourAC'; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Rational _rationalFromDouble(double value, {int maxPrecision = 12}) { | ||||||
|  |     // 限制小数精度,避免无限循环小数 | ||||||
|  |     final str = value.toStringAsFixed(maxPrecision); | ||||||
|  |  | ||||||
|  |     if (!str.contains('.')) { | ||||||
|  |       return Rational.parse(str); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     final parts = str.split('.'); | ||||||
|  |     final integerPart = parts[0]; | ||||||
|  |     final fractionalPart = parts[1]; | ||||||
|  |  | ||||||
|  |     final numerator = BigInt.parse(integerPart + fractionalPart); | ||||||
|  |     final denominator = BigInt.from(10).pow(fractionalPart.length); | ||||||
|  |  | ||||||
|  |     return Rational(numerator, denominator); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -424,6 +424,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "6.1.5+1" |     version: "6.1.5+1" | ||||||
|  |   rational: | ||||||
|  |     dependency: "direct main" | ||||||
|  |     description: | ||||||
|  |       name: rational | ||||||
|  |       sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.2.3" | ||||||
|   sky_engine: |   sky_engine: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: flutter |     description: flutter | ||||||
|   | |||||||
| @@ -39,6 +39,7 @@ dependencies: | |||||||
|   google_fonts: ^6.3.1 |   google_fonts: ^6.3.1 | ||||||
|   go_router: ^14.2.0 |   go_router: ^14.2.0 | ||||||
|   url_launcher: ^6.3.0 |   url_launcher: ^6.3.0 | ||||||
|  |   rational: ^2.2.3 | ||||||
|  |  | ||||||
| dev_dependencies: | dev_dependencies: | ||||||
|   flutter_test: |   flutter_test: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user