Support calculate ^0.5

This commit is contained in:
2025-09-14 13:42:14 +08:00
parent c9190d05a1
commit e6a52b8b74
4 changed files with 149 additions and 16 deletions

View File

@@ -1,5 +1,5 @@
// === 在 abstract class Expr 中添加声明 ===
import 'dart:math' show sqrt, cos, sin, tan;
import 'dart:math' show sqrt, cos, sin, tan, pow;
abstract class Expr {
Expr simplify();
@@ -488,7 +488,7 @@ class SqrtExpr extends Expr {
SqrtExpr(inner.substitute(varName, value));
@override
String toString() => "sqrt($inner)";
String toString() => "\\sqrt{${inner.toString()}}";
}
// === CosExpr ===
@@ -584,6 +584,90 @@ class TanExpr extends Expr {
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) 形式 ===
class _SqrtTerm {
final int coef;

View File

@@ -33,12 +33,12 @@ class Parser {
}
Expr parseMul() {
var expr = parseAtom();
var expr = parsePow();
skipSpaces();
while (!isEnd && (current == '*' || current == '/')) {
var op = current;
eat();
var right = parseAtom();
var right = parsePow();
if (op == '*') {
expr = MulExpr(expr, right);
} else {
@@ -49,6 +49,17 @@ class Parser {
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() {
skipSpaces();
if (current == '(') {
@@ -106,13 +117,20 @@ class Parser {
return VarExpr(varName);
}
// 解析
// 解析数字 (整数或小数)
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;
eat();
}
if (buf.isEmpty) throw Exception("无法解析: $current");
return IntExpr(int.parse(buf));
if (hasDot) {
return DoubleExpr(double.parse(buf));
} else {
return IntExpr(int.parse(buf));
}
}
}

View File

@@ -52,12 +52,17 @@ class SolverService {
/// 1. 求解简单表达式
CalculationResult _solveSimpleExpression(String input) {
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(
CalculationStep(
stepNumber: 1,
title: '表达式求值',
explanation: '这是一个标准的数学表达式,我们将直接计算其结果。',
formula: '\$\$$input\$\$',
formula: '\$\$$latexInput\$\$',
),
);
@@ -113,12 +118,17 @@ class SolverService {
/// 2. 求解一元一次方程
CalculationResult _solveLinearEquation(String input) {
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(
CalculationStep(
stepNumber: 0,
title: '原方程',
explanation: '这是一元一次方程。',
formula: '\$\$$input\$\$',
formula: '\$\$$latexInput\$\$',
),
);
@@ -1262,8 +1272,7 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
/// 格式化原始方程,保持符号形式
String _formatOriginalEquation(String input) {
// Simply return the original equation with proper LaTeX formatting
// This avoids complex parsing issues and preserves the original symbolic form
// Parse the equation and convert to LaTeX
String result = input.replaceAll(' ', '');
// 确保方程格式正确
@@ -1271,9 +1280,31 @@ ${b1}y &= ${c1 - a1 * x.toDouble()}
result = '$result=0';
}
// Replace sqrt with LaTeX format
result = result.replaceAll('sqrt(', '\\sqrt{');
result = result.replaceAll(')', '}');
final parts = result.split('=');
if (parts.length == 2) {
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\$\$';
}

View File

@@ -39,7 +39,7 @@ void main() {
test('非完全平方数', () {
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();
expect(
expr.evaluate().toString().replaceAll(' ', ''),
"((sqrt(2)/2)+1/2)",
"((\\sqrt{2}/2)+1/2)",
);
});
});