Add LogExpr, ExpExpr and etc

This commit is contained in:
2025-09-14 13:57:47 +08:00
parent ebe9f89c9b
commit 50857f2d2e
4 changed files with 281 additions and 69 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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}';

View File

@@ -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');
}
}