diff --git a/lib/calculator.dart b/lib/calculator.dart index 190d132..20cfedf 100644 --- a/lib/calculator.dart +++ b/lib/calculator.dart @@ -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; diff --git a/lib/parser.dart b/lib/parser.dart index bd7b9c0..7cb2fce 100644 --- a/lib/parser.dart +++ b/lib/parser.dart @@ -62,76 +62,121 @@ 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); - } - - // 解析数字 (整数或小数) - var buf = ''; - 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)); + expr = VarExpr(varName); } else { - return IntExpr(int.parse(buf)); + // 解析数字 (整数或小数) + var buf = ''; + 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) { + expr = DoubleExpr(double.parse(buf)); + } else { + expr = IntExpr(int.parse(buf)); + } } + if (negative) { + expr = SubExpr(IntExpr(0), expr); + } + return expr; } } diff --git a/lib/solver.dart b/lib/solver.dart index 53da5d4..199336f 100644 --- a/lib/solver.dart +++ b/lib/solver.dart @@ -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}'; diff --git a/test_expand.dart b/test_expand.dart deleted file mode 100644 index ad93f5f..0000000 --- a/test_expand.dart +++ /dev/null @@ -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'); - } -}