✨ Add LogExpr, ExpExpr and etc
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// === 在 abstract class Expr 中添加声明 ===
|
// === 在 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';
|
import 'parser.dart';
|
||||||
|
|
||||||
abstract class Expr {
|
abstract class Expr {
|
||||||
@@ -621,8 +621,9 @@ class PowExpr extends Expr {
|
|||||||
// 1^x = 1
|
// 1^x = 1
|
||||||
if (l is IntExpr && l.value == 1) return IntExpr(1);
|
if (l is IntExpr && l.value == 1) return IntExpr(1);
|
||||||
// 0^x = 0 (for x != 0)
|
// 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);
|
return IntExpr(0);
|
||||||
|
}
|
||||||
|
|
||||||
// If both are numbers, compute
|
// If both are numbers, compute
|
||||||
if (l is IntExpr && r is IntExpr) {
|
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) 形式 ===
|
// === 辅助:识别 a * sqrt(X) 形式 ===
|
||||||
class _SqrtTerm {
|
class _SqrtTerm {
|
||||||
final int coef;
|
final int coef;
|
||||||
|
121
lib/parser.dart
121
lib/parser.dart
@@ -62,76 +62,121 @@ class Parser {
|
|||||||
|
|
||||||
Expr parseAtom() {
|
Expr parseAtom() {
|
||||||
skipSpaces();
|
skipSpaces();
|
||||||
|
bool negative = false;
|
||||||
|
if (current == '-') {
|
||||||
|
negative = true;
|
||||||
|
eat();
|
||||||
|
skipSpaces();
|
||||||
|
}
|
||||||
|
Expr expr;
|
||||||
if (current == '(') {
|
if (current == '(') {
|
||||||
eat();
|
eat();
|
||||||
var expr = parse();
|
expr = parse();
|
||||||
if (current != ')') throw Exception("缺少 )");
|
if (current != ')') throw Exception("缺少 )");
|
||||||
eat();
|
eat();
|
||||||
return expr;
|
} else if (input.startsWith("sqrt", pos)) {
|
||||||
}
|
|
||||||
|
|
||||||
if (input.startsWith("sqrt", pos)) {
|
|
||||||
pos += 4;
|
pos += 4;
|
||||||
if (current != '(') throw Exception("sqrt 缺少 (");
|
if (current != '(') throw Exception("sqrt 缺少 (");
|
||||||
eat();
|
eat();
|
||||||
var inner = parse();
|
var inner = parse();
|
||||||
if (current != ')') throw Exception("sqrt 缺少 )");
|
if (current != ')') throw Exception("sqrt 缺少 )");
|
||||||
eat();
|
eat();
|
||||||
return SqrtExpr(inner);
|
expr = SqrtExpr(inner);
|
||||||
}
|
} else if (input.startsWith("cos", pos)) {
|
||||||
|
|
||||||
if (input.startsWith("cos", pos)) {
|
|
||||||
pos += 3;
|
pos += 3;
|
||||||
if (current != '(') throw Exception("cos 缺少 (");
|
if (current != '(') throw Exception("cos 缺少 (");
|
||||||
eat();
|
eat();
|
||||||
var inner = parse();
|
var inner = parse();
|
||||||
if (current != ')') throw Exception("cos 缺少 )");
|
if (current != ')') throw Exception("cos 缺少 )");
|
||||||
eat();
|
eat();
|
||||||
return CosExpr(inner);
|
expr = CosExpr(inner);
|
||||||
}
|
} else if (input.startsWith("sin", pos)) {
|
||||||
|
|
||||||
if (input.startsWith("sin", pos)) {
|
|
||||||
pos += 3;
|
pos += 3;
|
||||||
if (current != '(') throw Exception("sin 缺少 (");
|
if (current != '(') throw Exception("sin 缺少 (");
|
||||||
eat();
|
eat();
|
||||||
var inner = parse();
|
var inner = parse();
|
||||||
if (current != ')') throw Exception("sin 缺少 )");
|
if (current != ')') throw Exception("sin 缺少 )");
|
||||||
eat();
|
eat();
|
||||||
return SinExpr(inner);
|
expr = SinExpr(inner);
|
||||||
}
|
} else if (input.startsWith("tan", pos)) {
|
||||||
|
|
||||||
if (input.startsWith("tan", pos)) {
|
|
||||||
pos += 3;
|
pos += 3;
|
||||||
if (current != '(') throw Exception("tan 缺少 (");
|
if (current != '(') throw Exception("tan 缺少 (");
|
||||||
eat();
|
eat();
|
||||||
var inner = parse();
|
var inner = parse();
|
||||||
if (current != ')') throw Exception("tan 缺少 )");
|
if (current != ')') throw Exception("tan 缺少 )");
|
||||||
eat();
|
eat();
|
||||||
return TanExpr(inner);
|
expr = TanExpr(inner);
|
||||||
}
|
} else if (input.startsWith("log", pos)) {
|
||||||
|
pos += 3;
|
||||||
// 解析变量 (单个字母)
|
if (current != '(') throw Exception("log 缺少 (");
|
||||||
if (RegExp(r'[a-zA-Z]').hasMatch(current)) {
|
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;
|
var varName = current;
|
||||||
eat();
|
eat();
|
||||||
return VarExpr(varName);
|
expr = 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));
|
|
||||||
} else {
|
} 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'package:flutter/foundation.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 'calculator.dart';
|
||||||
@@ -553,26 +554,26 @@ ${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)');
|
debugPrint('Expanding: ($factor1) * ($factor2)');
|
||||||
|
|
||||||
final coeffs1 = _parsePolynomial(factor1);
|
final coeffs1 = _parsePolynomial(factor1);
|
||||||
final coeffs2 = _parsePolynomial(factor2);
|
final coeffs2 = _parsePolynomial(factor2);
|
||||||
print('Coeffs1: $coeffs1, Coeffs2: $coeffs2');
|
debugPrint('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');
|
debugPrint('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');
|
debugPrint('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';
|
||||||
print('Expanded result: $expanded');
|
debugPrint('Expanded result: $expanded');
|
||||||
|
|
||||||
result = result.replaceFirst(factorMulMatch.group(0)!, expanded);
|
result = result.replaceFirst(factorMulMatch.group(0)!, expanded);
|
||||||
iterationCount++;
|
iterationCount++;
|
||||||
@@ -1033,7 +1034,7 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
|
|||||||
|
|
||||||
if (simplifiedB == 0) {
|
if (simplifiedB == 0) {
|
||||||
// Just the sqrt part with correct sign
|
// Just the sqrt part with correct sign
|
||||||
return isPlus ? '$sqrtExpr' : '-$sqrtExpr';
|
return isPlus ? sqrtExpr : '-$sqrtExpr';
|
||||||
} else if (simplifiedB == 1) {
|
} else if (simplifiedB == 1) {
|
||||||
// +1 * sqrt part
|
// +1 * sqrt part
|
||||||
return isPlus ? '1 + $sqrtExpr' : '1 - $sqrtExpr';
|
return isPlus ? '1 + $sqrtExpr' : '1 - $sqrtExpr';
|
||||||
@@ -1052,11 +1053,11 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Cannot simplify, use fraction form
|
// Cannot simplify, use fraction form
|
||||||
final bStr = b > 0 ? '${bInt}' : '(${bInt})';
|
final bStr = b > 0 ? '$bInt' : '($bInt)';
|
||||||
final signStr = isPlus ? '+' : '-';
|
final signStr = isPlus ? '+' : '-';
|
||||||
final numerator = b > 0
|
final numerator = b > 0
|
||||||
? '-$bStr $signStr $sqrtExpr'
|
? '-$bStr $signStr $sqrtExpr'
|
||||||
: '(${bInt}) $signStr $sqrtExpr';
|
: '($bInt) $signStr $sqrtExpr';
|
||||||
|
|
||||||
if (denominator == 2) {
|
if (denominator == 2) {
|
||||||
return '\\frac{$numerator}{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