♻️ Replaced with own calculator
This commit is contained in:
		
							
								
								
									
										167
									
								
								lib/solver.dart
									
									
									
									
									
								
							
							
						
						
									
										167
									
								
								lib/solver.dart
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | |||||||
| import 'dart:math'; | import 'dart:math'; | ||||||
| import 'package:flutter/foundation.dart'; // For kDebugMode |  | ||||||
| import 'package:math_expressions/math_expressions.dart'; |  | ||||||
| import 'package:rational/rational.dart'; | import 'package:rational/rational.dart'; | ||||||
| import 'models/calculation_step.dart'; | import 'models/calculation_step.dart'; | ||||||
|  | import 'calculator.dart'; | ||||||
|  | import 'parser.dart'; | ||||||
|  |  | ||||||
| /// 帮助解析一元一次方程 ax+b=cx+d 的辅助类 | /// 帮助解析一元一次方程 ax+b=cx+d 的辅助类 | ||||||
| class LinearEquationParts { | class LinearEquationParts { | ||||||
| @@ -43,9 +43,6 @@ class SolverService { | |||||||
|     try { |     try { | ||||||
|       return _solveSimpleExpression(input); // 使用原始输入以保留运算符 |       return _solveSimpleExpression(input); // 使用原始输入以保留运算符 | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       if (kDebugMode) { |  | ||||||
|         print(e); |  | ||||||
|       } |  | ||||||
|       throw Exception('无法识别的格式。请检查您的方程或表达式。'); |       throw Exception('无法识别的格式。请检查您的方程或表达式。'); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -76,17 +73,41 @@ class SolverService { | |||||||
|     // 预处理输入,将三角函数的参数从度转换为弧度 |     // 预处理输入,将三角函数的参数从度转换为弧度 | ||||||
|     String processedInput = _convertTrigToRadians(input); |     String processedInput = _convertTrigToRadians(input); | ||||||
|  |  | ||||||
|     GrammarParser p = GrammarParser(); |     try { | ||||||
|     Expression exp = p.parse(processedInput); |       // 使用自定义解析器解析表达式 | ||||||
|     final result = RealEvaluator().evaluate(exp).toDouble(); |       final parser = Parser(processedInput); | ||||||
|  |       final expr = parser.parse(); | ||||||
|  |  | ||||||
|     // 尝试将结果格式化为几倍根号的形式 |       // 对表达式进行求值 | ||||||
|     final formattedResult = _formatSqrtResult(result); |       final evaluatedExpr = expr.evaluate(); | ||||||
|  |  | ||||||
|     return CalculationResult( |       // 获取数值结果 - 需要正确进行类型转换 | ||||||
|       steps: steps, |       double result; | ||||||
|       finalAnswer: '\$\$$formattedResult\$\$', |       if (evaluatedExpr is IntExpr) { | ||||||
|     ); |         result = evaluatedExpr.value.toDouble(); | ||||||
|  |       } else if (evaluatedExpr is DoubleExpr) { | ||||||
|  |         result = evaluatedExpr.value; | ||||||
|  |       } else if (evaluatedExpr is FractionExpr) { | ||||||
|  |         result = evaluatedExpr.numerator / evaluatedExpr.denominator; | ||||||
|  |       } else { | ||||||
|  |         // 如果无法完全求值为数值,尝试简化并转换为字符串 | ||||||
|  |         final simplified = evaluatedExpr.simplify(); | ||||||
|  |         return CalculationResult( | ||||||
|  |           steps: steps, | ||||||
|  |           finalAnswer: '\$\$${simplified.toString()}\$\$', | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // 尝试将结果格式化为几倍根号的形式 | ||||||
|  |       final formattedResult = _formatSqrtResult(result); | ||||||
|  |  | ||||||
|  |       return CalculationResult( | ||||||
|  |         steps: steps, | ||||||
|  |         finalAnswer: '\$\$$formattedResult\$\$', | ||||||
|  |       ); | ||||||
|  |     } catch (e) { | ||||||
|  |       throw Exception('无法解析表达式: $input'); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /// 2. 求解一元一次方程 |   /// 2. 求解一元一次方程 | ||||||
| @@ -714,21 +735,28 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | |||||||
|       if (factorMulMatch != null) { |       if (factorMulMatch != null) { | ||||||
|         final factor1 = factorMulMatch.group(1)!; |         final factor1 = factorMulMatch.group(1)!; | ||||||
|         final factor2 = factorMulMatch.group(2)!; |         final factor2 = factorMulMatch.group(2)!; | ||||||
|  |         print('Expanding: ($factor1) * ($factor2)'); | ||||||
|  |  | ||||||
|         final coeffs1 = _parsePolynomial(factor1); |         final coeffs1 = _parsePolynomial(factor1); | ||||||
|         final coeffs2 = _parsePolynomial(factor2); |         final coeffs2 = _parsePolynomial(factor2); | ||||||
|  |         print('Coeffs1: $coeffs1, Coeffs2: $coeffs2'); | ||||||
|  |  | ||||||
|         final a = coeffs1[1] ?? 0; |         final a = coeffs1[1] ?? 0; | ||||||
|         final b = coeffs1[0] ?? 0; |         final b = coeffs1[0] ?? 0; | ||||||
|         final c = coeffs2[1] ?? 0; |         final c = coeffs2[1] ?? 0; | ||||||
|         final d = coeffs2[0] ?? 0; |         final d = coeffs2[0] ?? 0; | ||||||
|  |         print('a=$a, b=$b, c=$c, d=$d'); | ||||||
|  |  | ||||||
|         final newA = a * c; |         final newA = a * c; | ||||||
|         final newB = a * d + b * c; |         final newB = a * d + b * c; | ||||||
|         final newC = b * d; |         final newC = b * d; | ||||||
|  |         print('newA=$newA, newB=$newB, newC=$newC'); | ||||||
|  |  | ||||||
|         final expanded = |         final expanded = | ||||||
|             '${newA}x^2${newB >= 0 ? '+' : ''}${newB}x${newC >= 0 ? '+' : ''}$newC'; |             '${newA}x^2${newB >= 0 ? '+' : ''}${newB}x${newC >= 0 ? '+' : ''}$newC'; | ||||||
|         result = result.replaceFirst(factorMulMatch.group(0)!, '($expanded)'); |         print('Expanded result: $expanded'); | ||||||
|  |  | ||||||
|  |         result = result.replaceFirst(factorMulMatch.group(0)!, expanded); | ||||||
|         iterationCount++; |         iterationCount++; | ||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
| @@ -762,7 +790,11 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | |||||||
|         final newC = termB * factorB; |         final newC = termB * factorB; | ||||||
|  |  | ||||||
|         final expanded = |         final expanded = | ||||||
|             '${newA}x^2${newB >= 0 ? '+' : ''}${newB}x${newC >= 0 ? '+' : ''}$newC'; |             '${newA == 1 | ||||||
|  |                 ? '' | ||||||
|  |                 : newA == -1 | ||||||
|  |                 ? '-' | ||||||
|  |                 : newA}x^2${newB >= 0 ? '+' : ''}${newB}x${newC >= 0 ? '+' : ''}$newC'; | ||||||
|         result = result.replaceFirst(termFactorMatch.group(0)!, '($expanded)'); |         result = result.replaceFirst(termFactorMatch.group(0)!, '($expanded)'); | ||||||
|         iterationCount++; |         iterationCount++; | ||||||
|         continue; |         continue; | ||||||
| @@ -776,6 +808,54 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | |||||||
|       throw Exception('表达式展开过于复杂,请简化输入。'); |       throw Exception('表达式展开过于复杂,请简化输入。'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // 检查是否为方程(包含等号),如果是的话,将右边的常数项移到左边 | ||||||
|  |     if (result.contains('=')) { | ||||||
|  |       final parts = result.split('='); | ||||||
|  |       if (parts.length == 2) { | ||||||
|  |         final leftSide = parts[0]; | ||||||
|  |         final rightSide = parts[1]; | ||||||
|  |  | ||||||
|  |         // 解析左边的多项式 | ||||||
|  |         final leftCoeffs = _parsePolynomial(leftSide); | ||||||
|  |         final rightCoeffs = _parsePolynomial(rightSide); | ||||||
|  |  | ||||||
|  |         // 计算标准形式 ax^2 + bx + c = 0 的系数 | ||||||
|  |         // A = B 转换为 A - B = 0,所以右边的系数要取相反数 | ||||||
|  |         final a = (leftCoeffs[2] ?? 0) - (rightCoeffs[2] ?? 0); | ||||||
|  |         final b = (leftCoeffs[1] ?? 0) - (rightCoeffs[1] ?? 0); | ||||||
|  |         final c = (leftCoeffs[0] ?? 0) - (rightCoeffs[0] ?? 0); | ||||||
|  |  | ||||||
|  |         // 构建标准形式的方程 | ||||||
|  |         String standardForm = ''; | ||||||
|  |         if (a != 0) { | ||||||
|  |           standardForm += | ||||||
|  |               '${a == 1 | ||||||
|  |                   ? '' | ||||||
|  |                   : a == -1 | ||||||
|  |                   ? '-' | ||||||
|  |                   : a}x^2'; | ||||||
|  |         } | ||||||
|  |         if (b != 0) { | ||||||
|  |           standardForm += b > 0 ? '+${b}x' : '${b}x'; | ||||||
|  |         } | ||||||
|  |         if (c != 0) { | ||||||
|  |           standardForm += c > 0 ? '+$c' : '$c'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 移除开头的加号 | ||||||
|  |         if (standardForm.startsWith('+')) { | ||||||
|  |           standardForm = standardForm.substring(1); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 如果所有系数都为0,则方程恒成立 | ||||||
|  |         if (standardForm.isEmpty) { | ||||||
|  |           standardForm = '0'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         result = '$standardForm=0'; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -796,13 +876,24 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | |||||||
|  |  | ||||||
|   Map<int, double> _parsePolynomial(String side) { |   Map<int, double> _parsePolynomial(String side) { | ||||||
|     final coeffs = <int, double>{}; |     final coeffs = <int, double>{}; | ||||||
|  |  | ||||||
|  |     // 如果输入包含括号,去掉括号 | ||||||
|  |     var cleanSide = side; | ||||||
|  |     if (cleanSide.startsWith('(') && cleanSide.endsWith(')')) { | ||||||
|  |       cleanSide = cleanSide.substring(1, cleanSide.length - 1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // 扩展模式以支持 sqrt 函数 |     // 扩展模式以支持 sqrt 函数 | ||||||
|     final pattern = RegExp( |     final pattern = RegExp( | ||||||
|       r'([+-]?(?:\d*\.?\d*|sqrt\(\d+\)))x(?:\^(\d+))?|([+-]?(?:\d*\.?\d*|sqrt\(\d+\)))', |       r'([+-]?(?:\d*\.?\d*|sqrt\(\d+\)))x(?:\^(\d+))?|([+-]?(?:\d*\.?\d*|sqrt\(\d+\)))', | ||||||
|     ); |     ); | ||||||
|     var s = side.startsWith('+') || side.startsWith('-') ? side : '+$side'; |     var s = cleanSide.startsWith('+') || cleanSide.startsWith('-') | ||||||
|  |         ? cleanSide | ||||||
|  |         : '+$cleanSide'; | ||||||
|  |  | ||||||
|     for (final match in pattern.allMatches(s)) { |     for (final match in pattern.allMatches(s)) { | ||||||
|  |       if (match.group(0)!.isEmpty) continue; // Skip empty matches | ||||||
|  |  | ||||||
|       if (match.group(3) != null) { |       if (match.group(3) != null) { | ||||||
|         // 常数项 |         // 常数项 | ||||||
|         final constStr = match.group(3)!; |         final constStr = match.group(3)!; | ||||||
| @@ -893,26 +984,31 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | |||||||
|     if (a == 0) return null; |     if (a == 0) return null; | ||||||
|     int ac = a * c; |     int ac = a * c; | ||||||
|     int absAc = ac.abs(); |     int absAc = ac.abs(); | ||||||
|  |  | ||||||
|  |     // Try all divisors of abs(ac) and consider both positive and negative factors | ||||||
|     for (int d = 1; d <= sqrt(absAc).toInt(); d++) { |     for (int d = 1; d <= sqrt(absAc).toInt(); d++) { | ||||||
|       if (absAc % d == 0) { |       if (absAc % d == 0) { | ||||||
|         int d1 = d; |         int d1 = d; | ||||||
|         int d2 = absAc ~/ d; |         int d2 = absAc ~/ d; | ||||||
|         List<int> signs1 = ac >= 0 ? [1, -1] : [1, -1]; |  | ||||||
|         List<int> signs2 = ac >= 0 ? [1, -1] : [1, -1]; |         // Try all sign combinations for the factors | ||||||
|         for (int s1 in signs1) { |         // We need m * n = ac and m + n = b | ||||||
|           for (int s2 in signs2) { |         List<int> signCombinations = [1, -1]; | ||||||
|             int m = s1 * d1; |  | ||||||
|             int n = s2 * d2; |         for (int sign1 in signCombinations) { | ||||||
|             if (check(m, n, b)) return formatFactor(m, n, a); |           for (int sign2 in signCombinations) { | ||||||
|             m = s1 * d1; |             int m = sign1 * d1; | ||||||
|             n = s2 * (-d2); |             int n = sign2 * d2; | ||||||
|             if (check(m, n, b)) return formatFactor(m, n, a); |             if (m + n == b && m * n == ac) { | ||||||
|             m = s1 * (-d1); |               return formatFactor(m, n, a); | ||||||
|             n = s2 * d2; |             } | ||||||
|             if (check(m, n, b)) return formatFactor(m, n, a); |  | ||||||
|             m = s1 * (-d1); |             // Also try the swapped version | ||||||
|             n = s2 * (-d2); |             m = sign1 * d2; | ||||||
|             if (check(m, n, b)) return formatFactor(m, n, a); |             n = sign2 * d1; | ||||||
|  |             if (m + n == b && m * n == ac) { | ||||||
|  |               return formatFactor(m, n, a); | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @@ -1109,10 +1205,11 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | |||||||
|     for (int i = 0; i < s.length; i++) { |     for (int i = 0; i < s.length; i++) { | ||||||
|       final char = s[i]; |       final char = s[i]; | ||||||
|  |  | ||||||
|       if (char == '(') |       if (char == '(') { | ||||||
|         parenDepth++; |         parenDepth++; | ||||||
|       else if (char == ')') |       } else if (char == ')') { | ||||||
|         parenDepth--; |         parenDepth--; | ||||||
|  |       } | ||||||
|  |  | ||||||
|       // Only split on + or - when not inside parentheses |       // Only split on + or - when not inside parentheses | ||||||
|       if (parenDepth == 0 && (char == '+' || char == '-') && i > start) { |       if (parenDepth == 0 && (char == '+' || char == '-') && i > start) { | ||||||
|   | |||||||
							
								
								
									
										62
									
								
								test/core_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								test/core_test.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | import 'package:test/test.dart'; | ||||||
|  | import 'package:simple_math_calc/calculator.dart'; | ||||||
|  | import 'package:simple_math_calc/parser.dart'; | ||||||
|  |  | ||||||
|  | void main() { | ||||||
|  |   group('解析器测试', () { | ||||||
|  |     test('简单加法', () { | ||||||
|  |       final parser = Parser('2 + 3'); | ||||||
|  |       final expr = parser.parse(); | ||||||
|  |       final result = expr.evaluate(); | ||||||
|  |       expect(result.toString(), '5'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('乘法和加法优先级', () { | ||||||
|  |       final parser = Parser('2 + 3 * 4'); | ||||||
|  |       final expr = parser.parse(); | ||||||
|  |       final result = expr.evaluate(); | ||||||
|  |       expect(result.toString(), '14'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('括号优先级', () { | ||||||
|  |       final parser = Parser('(2 + 3) * 4'); | ||||||
|  |       final expr = parser.parse(); | ||||||
|  |       final result = expr.evaluate(); | ||||||
|  |       expect(result.toString(), '20'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('除法', () { | ||||||
|  |       final parser = Parser('10 / 2'); | ||||||
|  |       final expr = parser.parse(); | ||||||
|  |       final result = expr.evaluate(); | ||||||
|  |       expect(result.toString(), '5'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('平方根', () { | ||||||
|  |       final parser = Parser('sqrt(9)'); | ||||||
|  |       final expr = parser.parse(); | ||||||
|  |       final result = expr.evaluate(); | ||||||
|  |       expect(result.toString(), '3'); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   group('计算器测试', () { | ||||||
|  |     test('分数简化', () { | ||||||
|  |       final fraction = FractionExpr(4, 8); | ||||||
|  |       final simplified = fraction.simplify(); | ||||||
|  |       expect(simplified.toString(), '1/2'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('分数加法', () { | ||||||
|  |       final expr = AddExpr(FractionExpr(1, 2), FractionExpr(1, 4)); | ||||||
|  |       final result = expr.evaluate(); | ||||||
|  |       expect(result.toString(), '3/4'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('分数乘法', () { | ||||||
|  |       final expr = MulExpr(FractionExpr(1, 2), FractionExpr(2, 3)); | ||||||
|  |       final result = expr.evaluate(); | ||||||
|  |       expect(result.toString(), '1/3'); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										58
									
								
								test/solver_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								test/solver_test.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | import 'package:flutter/widgets.dart'; | ||||||
|  | import 'package:test/test.dart'; | ||||||
|  | import 'package:simple_math_calc/solver.dart'; | ||||||
|  |  | ||||||
|  | void main() { | ||||||
|  |   group('求解器测试', () { | ||||||
|  |     final solver = SolverService(); | ||||||
|  |  | ||||||
|  |     test('简单表达式求值', () { | ||||||
|  |       final result = solver.solve('2 + 3 * 4'); | ||||||
|  |       expect(result.finalAnswer, contains('14')); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('简单方程求解', () { | ||||||
|  |       final result = solver.solve('2x + 3 = 7'); | ||||||
|  |       expect(result.finalAnswer, contains('x = 2')); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('二次方程求解', () { | ||||||
|  |       final result = solver.solve('x^2 - 5x + 6 = 0'); | ||||||
|  |       debugPrint(result.finalAnswer); | ||||||
|  |       expect( | ||||||
|  |         result.finalAnswer.contains('x_1 = 2') && | ||||||
|  |             result.finalAnswer.contains('x_2 = 3'), | ||||||
|  |         true, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('三角函数求值', () { | ||||||
|  |       final result = solver.solve('sin(30)'); | ||||||
|  |       debugPrint(result.finalAnswer); | ||||||
|  |       expect(result.finalAnswer.contains(r'\frac{1}{2}'), true); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('带括号的复杂表达式', () { | ||||||
|  |       final result = solver.solve('(2 + 3) * 4'); | ||||||
|  |       expect(result.finalAnswer, contains('20')); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     test('括号展开的二次方程', () { | ||||||
|  |       final result = solver.solve('(x+8)(x+1)=-12'); | ||||||
|  |       debugPrint('Result for (x+8)(x+1)=-12: ${result.finalAnswer}'); | ||||||
|  |       // 这个方程应该被识别为一元二次方程,正确解应该是 x = -4 或 x = -5 | ||||||
|  |       expect( | ||||||
|  |         result.steps.any((step) => step.title == '整理方程'), | ||||||
|  |         true, | ||||||
|  |         reason: '方程应被识别为一元二次方程并进行整理', | ||||||
|  |       ); | ||||||
|  |       expect( | ||||||
|  |         (result.finalAnswer.contains('-4') && | ||||||
|  |                 result.finalAnswer.contains('-5')) || | ||||||
|  |             result.finalAnswer.contains('x = -4') || | ||||||
|  |             result.finalAnswer.contains('x = -5'), | ||||||
|  |         true, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								test_expand.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								test_expand.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import 'lib/solver.dart'; | ||||||
|  |  | ||||||
|  | void main() { | ||||||
|  |   final solver = SolverService(); | ||||||
|  |  | ||||||
|  |   // Test the problematic case | ||||||
|  |   final input = '(x+8)(x+1)=-12'; | ||||||
|  |   print('Input: $input'); | ||||||
|  |  | ||||||
|  |   try { | ||||||
|  |     final result = solver.solve(input); | ||||||
|  |     print('Result: ${result.finalAnswer}'); | ||||||
|  |     print('Steps:'); | ||||||
|  |     for (final step in result.steps) { | ||||||
|  |       print('Step ${step.stepNumber}: ${step.title}'); | ||||||
|  |       print('  Formula: ${step.formula}'); | ||||||
|  |     } | ||||||
|  |   } catch (e) { | ||||||
|  |     print('Error: $e'); | ||||||
|  |   } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user