use super::*;
mod leibniz_notation {
use super::*;
#[test]
fn first_derivative_dy_dx() {
let expr = parse("dy/dx").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn first_derivative_df_dx() {
let expr = parse("df/dx").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn first_derivative_dz_dt() {
let expr = parse("dz/dt").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("z".to_string()));
assert_eq!(var, "t");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn second_derivative_d2y_dx2() {
let expr = parse("d2y/dx2").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 2);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn third_derivative_d3y_dx3() {
let expr = parse("d3y/dx3").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 3);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn multi_letter_function_dtheta_dt() {
let expr = parse("dtheta/dt").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("theta".to_string()));
assert_eq!(var, "t");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn derivative_in_equation() {
let exprs = parse_equation_system("dy/dx = x*y").unwrap();
assert_eq!(exprs.len(), 1);
match &exprs[0].kind {
ExprKind::Equation { left, .. } => {
assert!(matches!(left.kind, ExprKind::Derivative { .. }));
}
other => panic!("Expected Relation, got {:?}", other),
}
}
#[test]
fn second_derivative_in_ode() {
let expr = parse("d2y/dx2 + 3*dy/dx + 2*y = 0").unwrap();
assert!(matches!(expr.kind, ExprKind::Equation { .. }));
}
#[test]
fn not_a_derivative_plain_division() {
let expr = parse("da/b").unwrap();
assert!(matches!(expr.kind, ExprKind::Binary { .. }));
}
}
mod prime_notation {
use super::*;
#[test]
fn first_derivative_y_prime() {
let expr = parse("y'").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn second_derivative_y_double_prime() {
let expr = parse("y''").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "");
assert_eq!(*order, 2);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn third_derivative_y_triple_prime() {
let expr = parse("y'''").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "");
assert_eq!(*order, 3);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn prime_in_equation() {
let expr = parse("y' = -y").unwrap();
match &expr.kind {
ExprKind::Equation { left, .. } => {
assert!(matches!(left.kind, ExprKind::Derivative { .. }));
}
other => panic!("Expected Relation, got {:?}", other),
}
}
#[test]
fn second_order_ode_prime() {
let expr = parse("y'' + y = 0").unwrap();
assert!(matches!(expr.kind, ExprKind::Equation { .. }));
}
#[test]
fn prime_on_different_variable() {
let expr = parse("f'").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
assert_eq!(var, "");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn prime_with_rhs_expression() {
let expr = parse("y' = -2*y + 3*x").unwrap();
assert!(matches!(expr.kind, ExprKind::Equation { .. }));
}
}
mod diff_function {
use super::*;
#[test]
fn diff_first_derivative() {
let expr = parse("diff(y, x)").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn diff_second_derivative() {
let expr = parse("diff(y, x, 2)").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 2);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn diff_third_derivative() {
let expr = parse("diff(y, x, 3)").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("y".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 3);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn diff_with_complex_expression() {
let expr = parse("diff(x^2 + y, x)").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert!(matches!(expr.kind, ExprKind::Binary { .. }));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn diff_in_equation() {
let expr = parse("diff(y, x) = x").unwrap();
match &expr.kind {
ExprKind::Equation { left, .. } => {
assert!(matches!(left.kind, ExprKind::Derivative { .. }));
}
other => panic!("Expected Relation, got {:?}", other),
}
}
#[test]
fn diff_in_larger_expression() {
let expr = parse("diff(y, x, 2) + y = 0").unwrap();
assert!(matches!(expr.kind, ExprKind::Equation { .. }));
}
}
mod partial_function {
use super::*;
#[test]
fn partial_first_order() {
let expr = parse("partial(f, x)").unwrap();
match &expr.kind {
ExprKind::PartialDerivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected PartialDerivative, got {:?}", other),
}
}
#[test]
fn partial_second_order() {
let expr = parse("partial(f, x, 2)").unwrap();
match &expr.kind {
ExprKind::PartialDerivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 2);
}
other => panic!("Expected PartialDerivative, got {:?}", other),
}
}
#[test]
fn partial_mixed_two_vars() {
let expr = parse("partial(f, x, y)").unwrap();
match &expr.kind {
ExprKind::PartialDerivative {
expr: inner,
var,
order,
} => {
assert_eq!(var, "x");
assert_eq!(*order, 1);
match &inner.kind {
ExprKind::PartialDerivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
assert_eq!(var, "y");
assert_eq!(*order, 1);
}
other => panic!("Expected inner PartialDerivative, got {:?}", other),
}
}
other => panic!("Expected PartialDerivative, got {:?}", other),
}
}
#[test]
fn partial_with_complex_expression() {
let expr = parse("partial(x^2*y, x)").unwrap();
match &expr.kind {
ExprKind::PartialDerivative { expr, var, order } => {
assert!(matches!(expr.kind, ExprKind::Binary { .. }));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected PartialDerivative, got {:?}", other),
}
}
#[test]
fn partial_in_equation() {
let expr = parse("partial(f, x) = 2*x*y").unwrap();
match &expr.kind {
ExprKind::Equation { left, .. } => {
assert!(matches!(left.kind, ExprKind::PartialDerivative { .. }));
}
other => panic!("Expected Relation, got {:?}", other),
}
}
}
mod gradient_notation {
use super::*;
#[test]
fn grad_with_parens() {
let expr = parse("grad(f)").unwrap();
match &expr.kind {
ExprKind::Gradient { expr } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
}
other => panic!("Expected Gradient, got {:?}", other),
}
}
#[test]
fn nabla_with_parens() {
let expr = parse("nabla(f)").unwrap();
match &expr.kind {
ExprKind::Gradient { expr } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
}
other => panic!("Expected Gradient, got {:?}", other),
}
}
#[test]
fn unicode_nabla_with_identifier() {
let expr = parse("∇f").unwrap();
match &expr.kind {
ExprKind::Gradient { expr } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
}
other => panic!("Expected Gradient, got {:?}", other),
}
}
#[test]
fn unicode_nabla_with_parens() {
let expr = parse("∇(x^2 + y^2)").unwrap();
match &expr.kind {
ExprKind::Gradient { expr } => {
assert!(matches!(expr.kind, ExprKind::Binary { .. }));
}
other => panic!("Expected Gradient, got {:?}", other),
}
}
#[test]
fn nabla_with_complex_expression() {
let expr = parse("nabla(x^2*y + z)").unwrap();
match &expr.kind {
ExprKind::Gradient { expr } => {
assert!(matches!(expr.kind, ExprKind::Binary { .. }));
}
other => panic!("Expected Gradient, got {:?}", other),
}
}
#[test]
fn grad_without_parens() {
let expr = parse("grad f").unwrap();
match &expr.kind {
ExprKind::Gradient { expr } => {
assert_eq!(**expr, Expression::variable("f".to_string()));
}
other => panic!("Expected Gradient, got {:?}", other),
}
}
#[test]
fn nabla_in_equation() {
let expr = parse("∇f = 0").unwrap();
match &expr.kind {
ExprKind::Equation { left, .. } => {
assert!(matches!(left.kind, ExprKind::Gradient { .. }));
}
other => panic!("Expected Equation, got {:?}", other),
}
}
}
mod integrate_function {
use super::*;
#[test]
fn indefinite_with_dx() {
let expr = parse("integrate(x^2, dx)").unwrap();
match &expr.kind {
ExprKind::Integral {
integrand,
var,
bounds,
} => {
assert!(matches!(integrand.kind, ExprKind::Binary { .. }));
assert_eq!(var, "x");
assert!(bounds.is_none());
}
other => panic!("Expected Integral, got {:?}", other),
}
}
#[test]
fn indefinite_with_bare_var() {
let expr = parse("integrate(sin(x), x)").unwrap();
match &expr.kind {
ExprKind::Integral { integrand, var, .. } => {
assert!(matches!(integrand.kind, ExprKind::Function { .. }));
assert_eq!(var, "x");
}
other => panic!("Expected Integral, got {:?}", other),
}
}
#[test]
fn definite_with_bounds() {
let expr = parse("integrate(x, dx, 0, 1)").unwrap();
match &expr.kind {
ExprKind::Integral { var, bounds, .. } => {
assert_eq!(var, "x");
let b = bounds.as_ref().unwrap();
assert_eq!(*b.lower, Expression::integer(0));
assert_eq!(*b.upper, Expression::integer(1));
}
other => panic!("Expected Integral, got {:?}", other),
}
}
#[test]
fn definite_with_symbolic_bounds() {
let expr = parse("integrate(sin(x), dx, 0, pi)").unwrap();
match &expr.kind {
ExprKind::Integral { var, bounds, .. } => {
assert_eq!(var, "x");
let b = bounds.as_ref().unwrap();
assert_eq!(*b.upper, Expression::constant(MathConstant::Pi));
}
other => panic!("Expected Integral, got {:?}", other),
}
}
#[test]
fn alias_integral() {
let expr = parse("integral(x, dx)").unwrap();
assert!(matches!(expr.kind, ExprKind::Integral { .. }));
}
#[test]
fn alias_int() {
let expr = parse("int(x, dx)").unwrap();
assert!(matches!(expr.kind, ExprKind::Integral { .. }));
}
#[test]
fn in_equation() {
let expr = parse("integrate(f, dx, a, b) = F(b) - F(a)").unwrap();
assert!(matches!(expr.kind, ExprKind::Equation { .. }));
}
}
mod sum_function {
use super::*;
#[test]
fn basic_sum() {
let expr = parse("sum(i^2, i, 1, n)").unwrap();
match &expr.kind {
ExprKind::Sum {
index,
lower,
upper,
body,
} => {
assert_eq!(index, "i");
assert_eq!(**lower, Expression::integer(1));
assert_eq!(**upper, Expression::variable("n".to_string()));
assert!(matches!(body.kind, ExprKind::Binary { .. }));
}
other => panic!("Expected Sum, got {:?}", other),
}
}
#[test]
fn sum_with_infinity() {
let expr = parse("sum(1/n, n, 1, inf)").unwrap();
match &expr.kind {
ExprKind::Sum { upper, .. } => {
assert_eq!(**upper, Expression::constant(MathConstant::Infinity));
}
other => panic!("Expected Sum, got {:?}", other),
}
}
#[test]
fn alias_summation() {
let expr = parse("summation(k, k, 0, 10)").unwrap();
assert!(matches!(expr.kind, ExprKind::Sum { .. }));
}
}
mod product_function {
use super::*;
#[test]
fn basic_product() {
let expr = parse("product(k, k, 1, n)").unwrap();
match &expr.kind {
ExprKind::Product {
index,
lower,
upper,
body,
} => {
assert_eq!(index, "k");
assert_eq!(**lower, Expression::integer(1));
assert_eq!(**upper, Expression::variable("n".to_string()));
assert_eq!(**body, Expression::variable("k".to_string()));
}
other => panic!("Expected Product, got {:?}", other),
}
}
#[test]
fn alias_prod() {
let expr = parse("prod(i, i, 1, 5)").unwrap();
assert!(matches!(expr.kind, ExprKind::Product { .. }));
}
}
mod limit_function {
use super::*;
use crate::ast::Direction;
#[test]
fn two_sided_limit() {
let expr = parse("limit(sin(x)/x, x, 0)").unwrap();
match &expr.kind {
ExprKind::Limit {
expr,
var,
to,
direction,
} => {
assert!(matches!(expr.kind, ExprKind::Binary { .. }));
assert_eq!(var, "x");
assert_eq!(**to, Expression::integer(0));
assert_eq!(*direction, Direction::Both);
}
other => panic!("Expected Limit, got {:?}", other),
}
}
#[test]
fn right_hand_limit_plus() {
let expr = parse("limit(1/x, x, 0, +)").unwrap();
match &expr.kind {
ExprKind::Limit { direction, .. } => {
assert_eq!(*direction, Direction::Right);
}
other => panic!("Expected Limit, got {:?}", other),
}
}
#[test]
fn left_hand_limit_minus() {
let expr = parse("limit(1/x, x, 0, -)").unwrap();
match &expr.kind {
ExprKind::Limit { direction, .. } => {
assert_eq!(*direction, Direction::Left);
}
other => panic!("Expected Limit, got {:?}", other),
}
}
#[test]
fn limit_at_infinity() {
let expr = parse("limit(1/x, x, inf)").unwrap();
match &expr.kind {
ExprKind::Limit { to, .. } => {
assert_eq!(**to, Expression::constant(MathConstant::Infinity));
}
other => panic!("Expected Limit, got {:?}", other),
}
}
#[test]
fn alias_lim() {
let expr = parse("lim(f, x, 0)").unwrap();
assert!(matches!(expr.kind, ExprKind::Limit { .. }));
}
#[test]
fn direction_keyword_right() {
let expr = parse("limit(f, x, 0, right)").unwrap();
match &expr.kind {
ExprKind::Limit { direction, .. } => {
assert_eq!(*direction, Direction::Right);
}
other => panic!("Expected Limit, got {:?}", other),
}
}
#[test]
fn direction_keyword_left() {
let expr = parse("limit(f, x, 0, left)").unwrap();
match &expr.kind {
ExprKind::Limit { direction, .. } => {
assert_eq!(*direction, Direction::Left);
}
other => panic!("Expected Limit, got {:?}", other),
}
}
}
mod operator_derivative {
use super::*;
#[test]
fn d_expr_over_dx() {
let expr = parse("d(x^2)/dx").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert!(matches!(expr.kind, ExprKind::Binary { .. }));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn d_sin_over_dx() {
let expr = parse("d(sin(x))/dx").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert!(matches!(expr.kind, ExprKind::Function { .. }));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn d_expr_over_d_paren_var() {
let expr = parse("d(omega)/d(k)").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("omega".to_string()));
assert_eq!(var, "k");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn d_u_over_dx() {
let expr = parse("d(U)/dx").unwrap();
match &expr.kind {
ExprKind::Derivative { expr, var, order } => {
assert_eq!(**expr, Expression::variable("U".to_string()));
assert_eq!(var, "x");
assert_eq!(*order, 1);
}
other => panic!("Expected Derivative, got {:?}", other),
}
}
#[test]
fn in_equation() {
let expr = parse("d(x^2)/dx = 2*x").unwrap();
assert!(matches!(expr.kind, ExprKind::Equation { .. }));
}
}