✨ Support calculate ^0.5
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// === 在 abstract class Expr 中添加声明 ===
|
// === 在 abstract class Expr 中添加声明 ===
|
||||||
import 'dart:math' show sqrt, cos, sin, tan;
|
import 'dart:math' show sqrt, cos, sin, tan, pow;
|
||||||
|
|
||||||
abstract class Expr {
|
abstract class Expr {
|
||||||
Expr simplify();
|
Expr simplify();
|
||||||
@@ -488,7 +488,7 @@ class SqrtExpr extends Expr {
|
|||||||
SqrtExpr(inner.substitute(varName, value));
|
SqrtExpr(inner.substitute(varName, value));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "sqrt($inner)";
|
String toString() => "\\sqrt{${inner.toString()}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// === CosExpr ===
|
// === CosExpr ===
|
||||||
@@ -584,6 +584,90 @@ class TanExpr extends Expr {
|
|||||||
String toString() => "tan($inner)";
|
String toString() => "tan($inner)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === PowExpr ===
|
||||||
|
class PowExpr extends Expr {
|
||||||
|
final Expr left, right;
|
||||||
|
PowExpr(this.left, this.right);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr simplify() {
|
||||||
|
var l = left.simplify();
|
||||||
|
var r = right.simplify();
|
||||||
|
|
||||||
|
// x^0 = 1
|
||||||
|
if (r is IntExpr && r.value == 0) return IntExpr(1);
|
||||||
|
// x^1 = x
|
||||||
|
if (r is IntExpr && r.value == 1) return l;
|
||||||
|
// 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)) {
|
||||||
|
return IntExpr(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PowExpr(l, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr evaluate() {
|
||||||
|
var l = left.evaluate();
|
||||||
|
var r = right.evaluate();
|
||||||
|
|
||||||
|
// x^0 = 1
|
||||||
|
if (r is IntExpr && r.value == 0) return IntExpr(1);
|
||||||
|
// x^1 = x
|
||||||
|
if (r is IntExpr && r.value == 1) return l;
|
||||||
|
// 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))
|
||||||
|
return IntExpr(0);
|
||||||
|
|
||||||
|
// If both are numbers, compute
|
||||||
|
if (l is IntExpr && r is IntExpr) {
|
||||||
|
return DoubleExpr(pow(l.value.toDouble(), r.value.toDouble()).toDouble());
|
||||||
|
}
|
||||||
|
if (l is DoubleExpr && r is IntExpr) {
|
||||||
|
return DoubleExpr(pow(l.value, r.value.toDouble()).toDouble());
|
||||||
|
}
|
||||||
|
if (l is IntExpr && r is DoubleExpr) {
|
||||||
|
return DoubleExpr(pow(l.value.toDouble(), r.value).toDouble());
|
||||||
|
}
|
||||||
|
if (l is DoubleExpr && r is DoubleExpr) {
|
||||||
|
return DoubleExpr(pow(l.value, r.value).toDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
return PowExpr(l, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => PowExpr(
|
||||||
|
left.substitute(varName, value),
|
||||||
|
right.substitute(varName, value),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
String leftStr = left.toString();
|
||||||
|
String rightStr = right.toString();
|
||||||
|
|
||||||
|
// Remove outer parentheses
|
||||||
|
if (leftStr.startsWith('(') && leftStr.endsWith(')')) {
|
||||||
|
leftStr = leftStr.substring(1, leftStr.length - 1);
|
||||||
|
}
|
||||||
|
if (rightStr.startsWith('(') && rightStr.endsWith(')')) {
|
||||||
|
rightStr = rightStr.substring(1, rightStr.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add parentheses around base if it's a complex expression
|
||||||
|
bool needsParens =
|
||||||
|
!(left is VarExpr || left is IntExpr || left is DoubleExpr);
|
||||||
|
String base = needsParens ? '($leftStr)' : leftStr;
|
||||||
|
|
||||||
|
return '$base^{$rightStr}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === 辅助:识别 a * sqrt(X) 形式 ===
|
// === 辅助:识别 a * sqrt(X) 形式 ===
|
||||||
class _SqrtTerm {
|
class _SqrtTerm {
|
||||||
final int coef;
|
final int coef;
|
||||||
|
@@ -33,12 +33,12 @@ class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expr parseMul() {
|
Expr parseMul() {
|
||||||
var expr = parseAtom();
|
var expr = parsePow();
|
||||||
skipSpaces();
|
skipSpaces();
|
||||||
while (!isEnd && (current == '*' || current == '/')) {
|
while (!isEnd && (current == '*' || current == '/')) {
|
||||||
var op = current;
|
var op = current;
|
||||||
eat();
|
eat();
|
||||||
var right = parseAtom();
|
var right = parsePow();
|
||||||
if (op == '*') {
|
if (op == '*') {
|
||||||
expr = MulExpr(expr, right);
|
expr = MulExpr(expr, right);
|
||||||
} else {
|
} else {
|
||||||
@@ -49,6 +49,17 @@ class Parser {
|
|||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expr parsePow() {
|
||||||
|
var expr = parseAtom();
|
||||||
|
skipSpaces();
|
||||||
|
if (!isEnd && current == '^') {
|
||||||
|
eat();
|
||||||
|
var right = parsePow(); // right associative
|
||||||
|
return PowExpr(expr, right);
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
Expr parseAtom() {
|
Expr parseAtom() {
|
||||||
skipSpaces();
|
skipSpaces();
|
||||||
if (current == '(') {
|
if (current == '(') {
|
||||||
@@ -106,13 +117,20 @@ class Parser {
|
|||||||
return VarExpr(varName);
|
return VarExpr(varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析整数
|
// 解析数字 (整数或小数)
|
||||||
var buf = '';
|
var buf = '';
|
||||||
while (!isEnd && RegExp(r'\d').hasMatch(current)) {
|
bool hasDot = false;
|
||||||
|
while (!isEnd &&
|
||||||
|
(RegExp(r'\d').hasMatch(current) || (!hasDot && current == '.'))) {
|
||||||
|
if (current == '.') hasDot = true;
|
||||||
buf += current;
|
buf += current;
|
||||||
eat();
|
eat();
|
||||||
}
|
}
|
||||||
if (buf.isEmpty) throw Exception("无法解析: $current");
|
if (buf.isEmpty) throw Exception("无法解析: $current");
|
||||||
return IntExpr(int.parse(buf));
|
if (hasDot) {
|
||||||
|
return DoubleExpr(double.parse(buf));
|
||||||
|
} else {
|
||||||
|
return IntExpr(int.parse(buf));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,12 +52,17 @@ class SolverService {
|
|||||||
/// 1. 求解简单表达式
|
/// 1. 求解简单表达式
|
||||||
CalculationResult _solveSimpleExpression(String input) {
|
CalculationResult _solveSimpleExpression(String input) {
|
||||||
final steps = <CalculationStep>[];
|
final steps = <CalculationStep>[];
|
||||||
|
// Parse the input to get LaTeX-formatted version
|
||||||
|
final parser = Parser(input);
|
||||||
|
final parsedExpr = parser.parse();
|
||||||
|
final latexInput = parsedExpr.toString().replaceAll('*', '\\cdot');
|
||||||
|
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
stepNumber: 1,
|
stepNumber: 1,
|
||||||
title: '表达式求值',
|
title: '表达式求值',
|
||||||
explanation: '这是一个标准的数学表达式,我们将直接计算其结果。',
|
explanation: '这是一个标准的数学表达式,我们将直接计算其结果。',
|
||||||
formula: '\$\$$input\$\$',
|
formula: '\$\$$latexInput\$\$',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -113,12 +118,17 @@ class SolverService {
|
|||||||
/// 2. 求解一元一次方程
|
/// 2. 求解一元一次方程
|
||||||
CalculationResult _solveLinearEquation(String input) {
|
CalculationResult _solveLinearEquation(String input) {
|
||||||
final steps = <CalculationStep>[];
|
final steps = <CalculationStep>[];
|
||||||
|
// Parse the input to get LaTeX-formatted version
|
||||||
|
final parser = Parser(input);
|
||||||
|
final parsedExpr = parser.parse();
|
||||||
|
final latexInput = parsedExpr.toString().replaceAll('*', '\\cdot');
|
||||||
|
|
||||||
steps.add(
|
steps.add(
|
||||||
CalculationStep(
|
CalculationStep(
|
||||||
stepNumber: 0,
|
stepNumber: 0,
|
||||||
title: '原方程',
|
title: '原方程',
|
||||||
explanation: '这是一元一次方程。',
|
explanation: '这是一元一次方程。',
|
||||||
formula: '\$\$$input\$\$',
|
formula: '\$\$$latexInput\$\$',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1262,8 +1272,7 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
|
|||||||
|
|
||||||
/// 格式化原始方程,保持符号形式
|
/// 格式化原始方程,保持符号形式
|
||||||
String _formatOriginalEquation(String input) {
|
String _formatOriginalEquation(String input) {
|
||||||
// Simply return the original equation with proper LaTeX formatting
|
// Parse the equation and convert to LaTeX
|
||||||
// This avoids complex parsing issues and preserves the original symbolic form
|
|
||||||
String result = input.replaceAll(' ', '');
|
String result = input.replaceAll(' ', '');
|
||||||
|
|
||||||
// 确保方程格式正确
|
// 确保方程格式正确
|
||||||
@@ -1271,9 +1280,31 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
|
|||||||
result = '$result=0';
|
result = '$result=0';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace sqrt with LaTeX format
|
final parts = result.split('=');
|
||||||
result = result.replaceAll('sqrt(', '\\sqrt{');
|
if (parts.length == 2) {
|
||||||
result = result.replaceAll(')', '}');
|
try {
|
||||||
|
final leftParser = Parser(parts[0]);
|
||||||
|
final leftExpr = leftParser.parse();
|
||||||
|
final rightParser = Parser(parts[1]);
|
||||||
|
final rightExpr = rightParser.parse();
|
||||||
|
result =
|
||||||
|
'${leftExpr.toString().replaceAll('*', '\\cdot')}=${rightExpr.toString().replaceAll('*', '\\cdot')}';
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback to original if parsing fails
|
||||||
|
result = result.replaceAll('sqrt(', '\\sqrt{');
|
||||||
|
result = result.replaceAll(')', '}');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
final parser = Parser(result.split('=')[0]);
|
||||||
|
final expr = parser.parse();
|
||||||
|
result = '${expr.toString().replaceAll('*', '\\cdot')}=0';
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback
|
||||||
|
result = result.replaceAll('sqrt(', '\\sqrt{');
|
||||||
|
result = result.replaceAll(')', '}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return '\$\$$result\$\$';
|
return '\$\$$result\$\$';
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,7 @@ void main() {
|
|||||||
|
|
||||||
test('非完全平方数', () {
|
test('非完全平方数', () {
|
||||||
var expr = Parser("sqrt(8)").parse();
|
var expr = Parser("sqrt(8)").parse();
|
||||||
expect(expr.simplify().toString().replaceAll(' ', ''), "(2*sqrt(2))");
|
expect(expr.simplify().toString().replaceAll(' ', ''), "(2*\\sqrt{2})");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ void main() {
|
|||||||
var expr = Parser("sqrt(8)/4 + 1/2").parse();
|
var expr = Parser("sqrt(8)/4 + 1/2").parse();
|
||||||
expect(
|
expect(
|
||||||
expr.evaluate().toString().replaceAll(' ', ''),
|
expr.evaluate().toString().replaceAll(' ', ''),
|
||||||
"((sqrt(2)/2)+1/2)",
|
"((\\sqrt{2}/2)+1/2)",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user