✨ 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