✨ Add LogExpr, ExpExpr and etc
This commit is contained in:
		| @@ -1,5 +1,5 @@ | ||||
| // === 在 abstract class Expr 中添加声明 === | ||||
| import 'dart:math' show sqrt, cos, sin, tan, pow; | ||||
| import 'dart:math' show sqrt, cos, sin, tan, pow, log, exp, asin, acos, atan; | ||||
| import 'parser.dart'; | ||||
|  | ||||
| abstract class Expr { | ||||
| @@ -621,8 +621,9 @@ class PowExpr extends Expr { | ||||
|     // 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)) | ||||
|     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) { | ||||
| @@ -669,6 +670,192 @@ class PowExpr extends Expr { | ||||
|   } | ||||
| } | ||||
|  | ||||
| // === LogExpr === | ||||
| class LogExpr extends Expr { | ||||
|   final Expr inner; | ||||
|   LogExpr(this.inner); | ||||
|  | ||||
|   @override | ||||
|   Expr simplify() => LogExpr(inner.simplify()); | ||||
|  | ||||
|   @override | ||||
|   Expr evaluate() { | ||||
|     var i = inner.evaluate(); | ||||
|     if (i is IntExpr) { | ||||
|       return DoubleExpr(log(i.value.toDouble())); | ||||
|     } | ||||
|     if (i is FractionExpr) { | ||||
|       return DoubleExpr(log(i.numerator / i.denominator)); | ||||
|     } | ||||
|     if (i is DoubleExpr) { | ||||
|       return DoubleExpr(log(i.value)); | ||||
|     } | ||||
|     return LogExpr(i); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Expr substitute(String varName, Expr value) => | ||||
|       LogExpr(inner.substitute(varName, value)); | ||||
|  | ||||
|   @override | ||||
|   String toString() => "log($inner)"; | ||||
| } | ||||
|  | ||||
| // === ExpExpr === | ||||
| class ExpExpr extends Expr { | ||||
|   final Expr inner; | ||||
|   ExpExpr(this.inner); | ||||
|  | ||||
|   @override | ||||
|   Expr simplify() => ExpExpr(inner.simplify()); | ||||
|  | ||||
|   @override | ||||
|   Expr evaluate() { | ||||
|     var i = inner.evaluate(); | ||||
|     if (i is IntExpr) { | ||||
|       return DoubleExpr(exp(i.value.toDouble())); | ||||
|     } | ||||
|     if (i is FractionExpr) { | ||||
|       return DoubleExpr(exp(i.numerator / i.denominator)); | ||||
|     } | ||||
|     if (i is DoubleExpr) { | ||||
|       return DoubleExpr(exp(i.value)); | ||||
|     } | ||||
|     return ExpExpr(i); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Expr substitute(String varName, Expr value) => | ||||
|       ExpExpr(inner.substitute(varName, value)); | ||||
|  | ||||
|   @override | ||||
|   String toString() => "exp($inner)"; | ||||
| } | ||||
|  | ||||
| // === AsinExpr === | ||||
| class AsinExpr extends Expr { | ||||
|   final Expr inner; | ||||
|   AsinExpr(this.inner); | ||||
|  | ||||
|   @override | ||||
|   Expr simplify() => AsinExpr(inner.simplify()); | ||||
|  | ||||
|   @override | ||||
|   Expr evaluate() { | ||||
|     var i = inner.evaluate(); | ||||
|     if (i is IntExpr) { | ||||
|       return DoubleExpr(asin(i.value.toDouble())); | ||||
|     } | ||||
|     if (i is FractionExpr) { | ||||
|       return DoubleExpr(asin(i.numerator / i.denominator)); | ||||
|     } | ||||
|     if (i is DoubleExpr) { | ||||
|       return DoubleExpr(asin(i.value)); | ||||
|     } | ||||
|     return AsinExpr(i); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Expr substitute(String varName, Expr value) => | ||||
|       AsinExpr(inner.substitute(varName, value)); | ||||
|  | ||||
|   @override | ||||
|   String toString() => "asin($inner)"; | ||||
| } | ||||
|  | ||||
| // === AcosExpr === | ||||
| class AcosExpr extends Expr { | ||||
|   final Expr inner; | ||||
|   AcosExpr(this.inner); | ||||
|  | ||||
|   @override | ||||
|   Expr simplify() => AcosExpr(inner.simplify()); | ||||
|  | ||||
|   @override | ||||
|   Expr evaluate() { | ||||
|     var i = inner.evaluate(); | ||||
|     if (i is IntExpr) { | ||||
|       return DoubleExpr(acos(i.value.toDouble())); | ||||
|     } | ||||
|     if (i is FractionExpr) { | ||||
|       return DoubleExpr(acos(i.numerator / i.denominator)); | ||||
|     } | ||||
|     if (i is DoubleExpr) { | ||||
|       return DoubleExpr(acos(i.value)); | ||||
|     } | ||||
|     return AcosExpr(i); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Expr substitute(String varName, Expr value) => | ||||
|       AcosExpr(inner.substitute(varName, value)); | ||||
|  | ||||
|   @override | ||||
|   String toString() => "acos($inner)"; | ||||
| } | ||||
|  | ||||
| // === AtanExpr === | ||||
| class AtanExpr extends Expr { | ||||
|   final Expr inner; | ||||
|   AtanExpr(this.inner); | ||||
|  | ||||
|   @override | ||||
|   Expr simplify() => AtanExpr(inner.simplify()); | ||||
|  | ||||
|   @override | ||||
|   Expr evaluate() { | ||||
|     var i = inner.evaluate(); | ||||
|     if (i is IntExpr) { | ||||
|       return DoubleExpr(atan(i.value.toDouble())); | ||||
|     } | ||||
|     if (i is FractionExpr) { | ||||
|       return DoubleExpr(atan(i.numerator / i.denominator)); | ||||
|     } | ||||
|     if (i is DoubleExpr) { | ||||
|       return DoubleExpr(atan(i.value)); | ||||
|     } | ||||
|     return AtanExpr(i); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Expr substitute(String varName, Expr value) => | ||||
|       AtanExpr(inner.substitute(varName, value)); | ||||
|  | ||||
|   @override | ||||
|   String toString() => "atan($inner)"; | ||||
| } | ||||
|  | ||||
| // === AbsExpr === | ||||
| class AbsExpr extends Expr { | ||||
|   final Expr inner; | ||||
|   AbsExpr(this.inner); | ||||
|  | ||||
|   @override | ||||
|   Expr simplify() => AbsExpr(inner.simplify()); | ||||
|  | ||||
|   @override | ||||
|   Expr evaluate() { | ||||
|     var i = inner.evaluate(); | ||||
|     if (i is IntExpr) { | ||||
|       return IntExpr(i.value.abs()); | ||||
|     } | ||||
|     if (i is FractionExpr) { | ||||
|       return FractionExpr(i.numerator.abs(), i.denominator); | ||||
|     } | ||||
|     if (i is DoubleExpr) { | ||||
|       return DoubleExpr(i.value.abs()); | ||||
|     } | ||||
|     return AbsExpr(i); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Expr substitute(String varName, Expr value) => | ||||
|       AbsExpr(inner.substitute(varName, value)); | ||||
|  | ||||
|   @override | ||||
|   String toString() => "|$inner|"; | ||||
| } | ||||
|  | ||||
| // === 辅助:识别 a * sqrt(X) 形式 === | ||||
| class _SqrtTerm { | ||||
|   final int coef; | ||||
|   | ||||
| @@ -62,61 +62,101 @@ class Parser { | ||||
|  | ||||
|   Expr parseAtom() { | ||||
|     skipSpaces(); | ||||
|     bool negative = false; | ||||
|     if (current == '-') { | ||||
|       negative = true; | ||||
|       eat(); | ||||
|       skipSpaces(); | ||||
|     } | ||||
|     Expr expr; | ||||
|     if (current == '(') { | ||||
|       eat(); | ||||
|       var expr = parse(); | ||||
|       expr = parse(); | ||||
|       if (current != ')') throw Exception("缺少 )"); | ||||
|       eat(); | ||||
|       return expr; | ||||
|     } | ||||
|  | ||||
|     if (input.startsWith("sqrt", pos)) { | ||||
|     } else if (input.startsWith("sqrt", pos)) { | ||||
|       pos += 4; | ||||
|       if (current != '(') throw Exception("sqrt 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("sqrt 缺少 )"); | ||||
|       eat(); | ||||
|       return SqrtExpr(inner); | ||||
|     } | ||||
|  | ||||
|     if (input.startsWith("cos", pos)) { | ||||
|       expr = SqrtExpr(inner); | ||||
|     } else if (input.startsWith("cos", pos)) { | ||||
|       pos += 3; | ||||
|       if (current != '(') throw Exception("cos 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("cos 缺少 )"); | ||||
|       eat(); | ||||
|       return CosExpr(inner); | ||||
|     } | ||||
|  | ||||
|     if (input.startsWith("sin", pos)) { | ||||
|       expr = CosExpr(inner); | ||||
|     } else if (input.startsWith("sin", pos)) { | ||||
|       pos += 3; | ||||
|       if (current != '(') throw Exception("sin 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("sin 缺少 )"); | ||||
|       eat(); | ||||
|       return SinExpr(inner); | ||||
|     } | ||||
|  | ||||
|     if (input.startsWith("tan", pos)) { | ||||
|       expr = SinExpr(inner); | ||||
|     } else if (input.startsWith("tan", pos)) { | ||||
|       pos += 3; | ||||
|       if (current != '(') throw Exception("tan 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("tan 缺少 )"); | ||||
|       eat(); | ||||
|       return TanExpr(inner); | ||||
|     } | ||||
|  | ||||
|     // 解析变量 (单个字母) | ||||
|     if (RegExp(r'[a-zA-Z]').hasMatch(current)) { | ||||
|       expr = TanExpr(inner); | ||||
|     } else if (input.startsWith("log", pos)) { | ||||
|       pos += 3; | ||||
|       if (current != '(') throw Exception("log 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("log 缺少 )"); | ||||
|       eat(); | ||||
|       expr = LogExpr(inner); | ||||
|     } else if (input.startsWith("exp", pos)) { | ||||
|       pos += 3; | ||||
|       if (current != '(') throw Exception("exp 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("exp 缺少 )"); | ||||
|       eat(); | ||||
|       expr = ExpExpr(inner); | ||||
|     } else if (input.startsWith("asin", pos)) { | ||||
|       pos += 4; | ||||
|       if (current != '(') throw Exception("asin 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("asin 缺少 )"); | ||||
|       eat(); | ||||
|       expr = AsinExpr(inner); | ||||
|     } else if (input.startsWith("acos", pos)) { | ||||
|       pos += 4; | ||||
|       if (current != '(') throw Exception("acos 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("acos 缺少 )"); | ||||
|       eat(); | ||||
|       expr = AcosExpr(inner); | ||||
|     } else if (input.startsWith("atan", pos)) { | ||||
|       pos += 4; | ||||
|       if (current != '(') throw Exception("atan 缺少 ("); | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != ')') throw Exception("atan 缺少 )"); | ||||
|       eat(); | ||||
|       expr = AtanExpr(inner); | ||||
|     } else if (current == '|') { | ||||
|       eat(); | ||||
|       var inner = parse(); | ||||
|       if (current != '|') throw Exception("abs 缺少 |"); | ||||
|       eat(); | ||||
|       expr = AbsExpr(inner); | ||||
|     } else if (RegExp(r'[a-zA-Z]').hasMatch(current)) { | ||||
|       var varName = current; | ||||
|       eat(); | ||||
|       return VarExpr(varName); | ||||
|     } | ||||
|  | ||||
|       expr = VarExpr(varName); | ||||
|     } else { | ||||
|       // 解析数字 (整数或小数) | ||||
|       var buf = ''; | ||||
|       bool hasDot = false; | ||||
| @@ -128,11 +168,16 @@ class Parser { | ||||
|       } | ||||
|       if (buf.isEmpty) throw Exception("无法解析: $current"); | ||||
|       if (hasDot) { | ||||
|       return DoubleExpr(double.parse(buf)); | ||||
|         expr = DoubleExpr(double.parse(buf)); | ||||
|       } else { | ||||
|       return IntExpr(int.parse(buf)); | ||||
|         expr = IntExpr(int.parse(buf)); | ||||
|       } | ||||
|     } | ||||
|     if (negative) { | ||||
|       expr = SubExpr(IntExpr(0), expr); | ||||
|     } | ||||
|     return expr; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// 计算角度表达式(如 30+45 = 75) | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import 'dart:math'; | ||||
| import 'package:flutter/foundation.dart'; | ||||
| import 'package:rational/rational.dart'; | ||||
| import 'models/calculation_step.dart'; | ||||
| import 'calculator.dart'; | ||||
| @@ -553,26 +554,26 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | ||||
|       if (factorMulMatch != null) { | ||||
|         final factor1 = factorMulMatch.group(1)!; | ||||
|         final factor2 = factorMulMatch.group(2)!; | ||||
|         print('Expanding: ($factor1) * ($factor2)'); | ||||
|         debugPrint('Expanding: ($factor1) * ($factor2)'); | ||||
|  | ||||
|         final coeffs1 = _parsePolynomial(factor1); | ||||
|         final coeffs2 = _parsePolynomial(factor2); | ||||
|         print('Coeffs1: $coeffs1, Coeffs2: $coeffs2'); | ||||
|         debugPrint('Coeffs1: $coeffs1, Coeffs2: $coeffs2'); | ||||
|  | ||||
|         final a = coeffs1[1] ?? 0; | ||||
|         final b = coeffs1[0] ?? 0; | ||||
|         final c = coeffs2[1] ?? 0; | ||||
|         final d = coeffs2[0] ?? 0; | ||||
|         print('a=$a, b=$b, c=$c, d=$d'); | ||||
|         debugPrint('a=$a, b=$b, c=$c, d=$d'); | ||||
|  | ||||
|         final newA = a * c; | ||||
|         final newB = a * d + b * c; | ||||
|         final newC = b * d; | ||||
|         print('newA=$newA, newB=$newB, newC=$newC'); | ||||
|         debugPrint('newA=$newA, newB=$newB, newC=$newC'); | ||||
|  | ||||
|         final expanded = | ||||
|             '${newA}x^2${newB >= 0 ? '+' : ''}${newB}x${newC >= 0 ? '+' : ''}$newC'; | ||||
|         print('Expanded result: $expanded'); | ||||
|         debugPrint('Expanded result: $expanded'); | ||||
|  | ||||
|         result = result.replaceFirst(factorMulMatch.group(0)!, expanded); | ||||
|         iterationCount++; | ||||
| @@ -1033,7 +1034,7 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | ||||
|  | ||||
|         if (simplifiedB == 0) { | ||||
|           // Just the sqrt part with correct sign | ||||
|           return isPlus ? '$sqrtExpr' : '-$sqrtExpr'; | ||||
|           return isPlus ? sqrtExpr : '-$sqrtExpr'; | ||||
|         } else if (simplifiedB == 1) { | ||||
|           // +1 * sqrt part | ||||
|           return isPlus ? '1 + $sqrtExpr' : '1 - $sqrtExpr'; | ||||
| @@ -1052,11 +1053,11 @@ ${b1}y &= ${c1 - a1 * x.toDouble()} | ||||
|         } | ||||
|       } else { | ||||
|         // Cannot simplify, use fraction form | ||||
|         final bStr = b > 0 ? '${bInt}' : '(${bInt})'; | ||||
|         final bStr = b > 0 ? '$bInt' : '($bInt)'; | ||||
|         final signStr = isPlus ? '+' : '-'; | ||||
|         final numerator = b > 0 | ||||
|             ? '-$bStr $signStr $sqrtExpr' | ||||
|             : '(${bInt}) $signStr $sqrtExpr'; | ||||
|             : '($bInt) $signStr $sqrtExpr'; | ||||
|  | ||||
|         if (denominator == 2) { | ||||
|           return '\\frac{$numerator}{2}'; | ||||
|   | ||||
| @@ -1,21 +0,0 @@ | ||||
| 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