mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-03-01 14:51:13 +01:00
Integration estimation
Needs more a bit more accuracy though. Currently the n value in the trapezoidal rule is fixed.
This commit is contained in:
parent
439f732013
commit
651b289f4b
kalk/src
kalk_cli/src
kalk_web/src
@ -302,6 +302,79 @@ fn eval_fn_call_expr(
|
|||||||
|
|
||||||
return Ok(sum);
|
return Ok(sum);
|
||||||
}
|
}
|
||||||
|
"integrate" | "∫" => {
|
||||||
|
// Make sure exactly 3 arguments were supplied.
|
||||||
|
if expressions.len() != 3 {
|
||||||
|
return Err(CalcError::IncorrectAmountOfArguments(
|
||||||
|
3,
|
||||||
|
"integrate".into(),
|
||||||
|
expressions.len(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = KalkNum::default();
|
||||||
|
let mut integration_variable: Option<&str> = None;
|
||||||
|
|
||||||
|
// integral(a, b, expr dx)
|
||||||
|
if let Expr::Binary(_, TokenKind::Star, right) = &expressions[2] {
|
||||||
|
if let Expr::Var(right_name) = &**right {
|
||||||
|
if right_name.starts_with("d") {
|
||||||
|
// Take the value, but remove the d, so that only eg. x is left from dx
|
||||||
|
integration_variable = Some(&right_name[1..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if integration_variable.is_none() {
|
||||||
|
unimplemented!(); // TODO: Error message
|
||||||
|
}
|
||||||
|
|
||||||
|
// delta_x/2[f(a) + 2f(x_1) + 2f(x_2) + ...2f(x_n) + f(b)]
|
||||||
|
// where delta_x = (b - a) / n
|
||||||
|
// and x_n = a + i * delta_x
|
||||||
|
|
||||||
|
// f(a)
|
||||||
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
|
integration_variable.unwrap().into(),
|
||||||
|
Box::new(expressions[0].clone()),
|
||||||
|
));
|
||||||
|
|
||||||
|
// "dx" is still in the expression. Set dx = 1, so that it doesn't affect the expression value.
|
||||||
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
|
String::from("dx"),
|
||||||
|
Box::new(Expr::Literal(1f64)),
|
||||||
|
));
|
||||||
|
|
||||||
|
result.value += eval_expr(context, &expressions[2], "")?.value;
|
||||||
|
|
||||||
|
// 2f(x_n)
|
||||||
|
// where x_n = a + i * delta_x
|
||||||
|
const N: i32 = 100;
|
||||||
|
let a = eval_expr(context, &expressions[0], "")?.value.to_f64();
|
||||||
|
let b = eval_expr(context, &expressions[1], "")?.value.to_f64();
|
||||||
|
let delta_x = (b - a) / N as f64;
|
||||||
|
for i in 1..N {
|
||||||
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
|
integration_variable.unwrap().into(),
|
||||||
|
Box::new(Expr::Literal(a + i as f64 * delta_x)),
|
||||||
|
));
|
||||||
|
|
||||||
|
// 2f(x_n)
|
||||||
|
result.value += 2 * eval_expr(context, &expressions[2], "")?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// f(b)
|
||||||
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
|
integration_variable.unwrap().into(),
|
||||||
|
Box::new(expressions[1].clone()),
|
||||||
|
));
|
||||||
|
result.value += eval_expr(context, &expressions[2], "")?.value;
|
||||||
|
|
||||||
|
// Finally, delta_x/2 for all of it
|
||||||
|
result.value *= delta_x / 2f64;
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,4 +623,22 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(interpret(stmt).unwrap().unwrap().to_f64(), result);
|
assert_eq!(interpret(stmt).unwrap().unwrap().to_f64(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integrate_fn() {
|
||||||
|
let stmt = Stmt::Expr(fn_call(
|
||||||
|
"integrate",
|
||||||
|
vec![
|
||||||
|
*literal(2f64),
|
||||||
|
*literal(4f64),
|
||||||
|
*binary(
|
||||||
|
binary(var("x"), TokenKind::Power, literal(3f64)),
|
||||||
|
TokenKind::Star,
|
||||||
|
binary(var("d"), TokenKind::Star, var("x")),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!((interpret(stmt).unwrap().unwrap().to_f64() - 60f64).abs() < 1f64);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ pub struct Context {
|
|||||||
parsing_identifier_stmt: bool,
|
parsing_identifier_stmt: bool,
|
||||||
equation_variable: Option<String>,
|
equation_variable: Option<String>,
|
||||||
contains_equal_sign: bool,
|
contains_equal_sign: bool,
|
||||||
|
is_in_integral: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@ -46,6 +47,7 @@ impl Context {
|
|||||||
parsing_identifier_stmt: false,
|
parsing_identifier_stmt: false,
|
||||||
equation_variable: None,
|
equation_variable: None,
|
||||||
contains_equal_sign: false,
|
contains_equal_sign: false,
|
||||||
|
is_in_integral: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
parse(&mut context, crate::prelude::INIT).unwrap();
|
parse(&mut context, crate::prelude::INIT).unwrap();
|
||||||
@ -491,6 +493,10 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
if !parse_as_var_instead && match_token(context, TokenKind::OpenParenthesis) {
|
if !parse_as_var_instead && match_token(context, TokenKind::OpenParenthesis) {
|
||||||
advance(context);
|
advance(context);
|
||||||
|
|
||||||
|
if identifier.value == "integrate" || identifier.value == "∫" {
|
||||||
|
context.is_in_integral = true;
|
||||||
|
}
|
||||||
|
|
||||||
let mut parameters = Vec::new();
|
let mut parameters = Vec::new();
|
||||||
parameters.push(parse_expr(context)?);
|
parameters.push(parse_expr(context)?);
|
||||||
|
|
||||||
@ -500,10 +506,16 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
consume(context, TokenKind::ClosedParenthesis)?;
|
consume(context, TokenKind::ClosedParenthesis)?;
|
||||||
|
context.is_in_integral = false;
|
||||||
|
|
||||||
return Ok(Expr::FnCall(identifier.value, parameters));
|
return Ok(Expr::FnCall(identifier.value, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eg. dx inside an integral, should be parsed as *one* identifier
|
||||||
|
if context.is_in_integral && identifier.value.starts_with("d") {
|
||||||
|
return Ok(Expr::Var(identifier.value));
|
||||||
|
}
|
||||||
|
|
||||||
// Eg. x
|
// Eg. x
|
||||||
if parse_as_var_instead || context.symbol_table.contains_var(&identifier.value) {
|
if parse_as_var_instead || context.symbol_table.contains_var(&identifier.value) {
|
||||||
Ok(Expr::Var(identifier.value))
|
Ok(Expr::Var(identifier.value))
|
||||||
|
@ -35,6 +35,8 @@ impl BinaryFuncInfo {
|
|||||||
pub fn is_prelude_func(identifier: &str) -> bool {
|
pub fn is_prelude_func(identifier: &str) -> bool {
|
||||||
identifier == "sum"
|
identifier == "sum"
|
||||||
|| identifier == "Σ"
|
|| identifier == "Σ"
|
||||||
|
|| identifier == "integrate"
|
||||||
|
|| identifier == "∫"
|
||||||
|| UNARY_FUNCS.contains_key(identifier)
|
|| UNARY_FUNCS.contains_key(identifier)
|
||||||
|| BINARY_FUNCS.contains_key(identifier)
|
|| BINARY_FUNCS.contains_key(identifier)
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ impl BinaryFuncInfo {
|
|||||||
pub fn is_prelude_func(identifier: &str) -> bool {
|
pub fn is_prelude_func(identifier: &str) -> bool {
|
||||||
identifier == "sum"
|
identifier == "sum"
|
||||||
|| identifier == "Σ"
|
|| identifier == "Σ"
|
||||||
|
|| identifier == "integrate"
|
||||||
|
|| identifier == "∫"
|
||||||
|| UNARY_FUNCS.contains_key(identifier)
|
|| UNARY_FUNCS.contains_key(identifier)
|
||||||
|| BINARY_FUNCS.contains_key(identifier)
|
|| BINARY_FUNCS.contains_key(identifier)
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@ lazy_static! {
|
|||||||
m.insert("floor", "⌊⌋");
|
m.insert("floor", "⌊⌋");
|
||||||
m.insert("gamma", "Γ");
|
m.insert("gamma", "Γ");
|
||||||
m.insert("sum", "Σ()");
|
m.insert("sum", "Σ()");
|
||||||
|
m.insert("integrate", "∫()");
|
||||||
m.insert("phi", "ϕ");
|
m.insert("phi", "ϕ");
|
||||||
m.insert("pi", "π");
|
m.insert("pi", "π");
|
||||||
m.insert("sqrt", "√");
|
m.insert("sqrt", "√");
|
||||||
@ -143,6 +144,7 @@ impl Completer for RLHelper {
|
|||||||
line.insert_str(line.pos(), elected);
|
line.insert_str(line.pos(), elected);
|
||||||
line.move_forward(match elected {
|
line.move_forward(match elected {
|
||||||
"Σ()" => 2,
|
"Σ()" => 2,
|
||||||
|
"∫()" => 2,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -177,6 +177,9 @@
|
|||||||
} else if (input == "Σ") {
|
} else if (input == "Σ") {
|
||||||
input += "()";
|
input += "()";
|
||||||
movementOffset = 2;
|
movementOffset = 2;
|
||||||
|
} else if (input == "∫") {
|
||||||
|
input += "()";
|
||||||
|
movementOffset = 2;
|
||||||
} else if (input == "⌊") {
|
} else if (input == "⌊") {
|
||||||
input += "⌋";
|
input += "⌋";
|
||||||
} else if (input == "⌈") {
|
} else if (input == "⌈") {
|
||||||
@ -283,6 +286,10 @@
|
|||||||
newSubstring = "Σ";
|
newSubstring = "Σ";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "integrate": {
|
||||||
|
newSubstring = "∫";
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "pi": {
|
case "pi": {
|
||||||
newSubstring = "π";
|
newSubstring = "π";
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user