Files
symclaw/crates/symclaw-core/tests/e2e.rs
T

191 lines
6.7 KiB
Rust

//! End-to-end tests: string input → final output.
use std::collections::HashMap;
use std::sync::Arc;
use symclaw_core::ast::Expr;
use symclaw_core::interner::Symbol;
use symclaw_core::{
codegen, differentiate, eval, integrate, limits, number_theory, parser, poly, proof, series,
simplify, solve,
};
fn parse(s: &str) -> Arc<Expr> {
parser::parse(s).unwrap()
}
// ── 1. Differentiate x^3 ───────────────────────────────────────
#[test]
fn e2e_differentiate_x_cubed() {
let expr = parse("x^3");
let deriv = differentiate::differentiate(&expr, Symbol::new("x"));
let simplified = simplify::simplify(&deriv);
let s = format!("{simplified}");
assert!(s.contains("3") && s.contains("x"), "d/dx(x^3) = {s}");
}
// ── 2. Integrate sin(x) ────────────────────────────────────────
#[test]
fn e2e_integrate_sin() {
let expr = parse("sin(x)");
let result = integrate::integrate(&expr, Symbol::new("x")).unwrap();
let s = format!("{result}");
assert!(s.contains("cos"), "∫sin(x)dx should contain cos: {s}");
}
// ── 3. Solve x^2 - 4 = 0 ───────────────────────────────────────
#[test]
fn e2e_solve_quadratic() {
let expr = parse("x^2 - 4");
let sols = solve::solve(&expr, Symbol::new("x"));
assert_eq!(sols.len(), 2, "x^2-4=0 should have 2 solutions");
let strs: Vec<String> = sols.iter().map(|s| format!("{s}")).collect();
assert!(strs.contains(&"2".to_owned()) || strs.iter().any(|s| s.contains("2")));
}
// ── 4. Limit sin(x)/x → 1 ──────────────────────────────────────
#[test]
fn e2e_limit_sinc() {
let expr = parse("sin(x)/x");
let result = limits::limit_at(&expr, "x", 0.0);
match result {
limits::LimitResult::Finite(e) => {
let s = format!("{e}");
assert!(s == "1" || s.starts_with("1"), "limit = {s}");
}
other => panic!("expected 1, got {other:?}"),
}
}
// ── 5. Taylor expansion of exp(x) ──────────────────────────────
#[test]
fn e2e_taylor_exp() {
let expr = parse("exp(x)");
let point = parse("0");
let expansion = series::taylor(&expr, Symbol::new("x"), &point, 5);
// Check it evaluates correctly at x=0.5
let mut vars = HashMap::new();
vars.insert(Symbol::new("x"), 0.5);
let val = eval::eval(&expansion, &vars).unwrap();
assert!((val - 0.5_f64.exp()).abs() < 0.01);
}
// ── 6. Simplify (x+1)*(x-1) ────────────────────────────────────
#[test]
fn e2e_simplify_expand() {
let expr = parse("(x+1)*(x-1)");
let simplified = simplify::simplify(&expr);
let s = format!("{simplified}");
// Should simplify to x^2 - 1 or equivalent
assert!(
s.contains("x") && (s.contains("-1") || s.contains("x^2")),
"got: {s}"
);
}
// ── 7. Proof trace ──────────────────────────────────────────────
#[test]
fn e2e_proof_trace() {
let expr = parse("x + 0");
let trace = proof::trace_simplify(&expr);
assert_eq!(trace.result, "x");
}
// ── 8. Codegen to Python ────────────────────────────────────────
#[test]
fn e2e_codegen_python() {
let expr = parse("x^2 + y");
let opts = codegen::CodegenOptions::new(codegen::Language::Python);
let code = codegen::generate(&expr, &opts);
assert!(
code.contains("**") || code.contains("pow"),
"Python code: {code}"
);
}
// ── 9. Number theory: is_prime ──────────────────────────────────
#[test]
fn e2e_is_prime() {
assert!(number_theory::is_prime(97));
assert!(!number_theory::is_prime(42));
}
// ── 10. Number theory: factorize ────────────────────────────────
#[test]
fn e2e_factorize() {
let factors = number_theory::factorize(360);
assert_eq!(factors, vec![(2, 3), (3, 2), (5, 1)]);
}
// ── 11. Polynomial conversion ───────────────────────────────────
#[test]
fn e2e_polynomial() {
let expr = parse("x^3 + 2*x^2 + x");
let p = poly::Polynomial::from_expr(&expr).unwrap();
let back = p.to_expr();
let s = format!("{}", Arc::new(back));
assert!(s.contains("x"), "poly roundtrip: {s}");
}
// ── 12. Solve linear equation ───────────────────────────────────
#[test]
fn e2e_solve_linear() {
let expr = parse("2*x - 6");
let sols = solve::solve(&expr, Symbol::new("x"));
assert!(!sols.is_empty());
let strs: Vec<String> = sols.iter().map(|s| format!("{s}")).collect();
assert!(strs.contains(&"3".to_owned()));
}
// ── 13. Differentiate trig ──────────────────────────────────────
#[test]
fn e2e_differentiate_cos() {
let expr = parse("cos(x)");
let deriv = differentiate::differentiate(&expr, Symbol::new("x"));
let s = format!("{}", simplify::simplify(&deriv));
assert!(s.contains("sin"), "d/dx(cos(x)) = {s}");
}
// ── 14. Eval with variables ─────────────────────────────────────
#[test]
fn e2e_eval_with_vars() {
let expr = parse("x^2 + y");
let mut vars = HashMap::new();
vars.insert(Symbol::new("x"), 3.0);
vars.insert(Symbol::new("y"), 1.0);
let val = eval::eval(&expr, &vars).unwrap();
assert!((val - 10.0).abs() < 1e-10);
}
// ── 15. Fibonacci ───────────────────────────────────────────────
#[test]
fn e2e_fibonacci() {
assert_eq!(number_theory::fibonacci(10), 55);
assert_eq!(number_theory::fibonacci(0), 0);
assert_eq!(number_theory::fibonacci(1), 1);
}
// ── 16. Simplify nested ────────────────────────────────────────
#[test]
fn e2e_simplify_nested() {
let expr = parse("(x * 1) + (0 + y)");
let simplified = simplify::simplify(&expr);
let s = format!("{simplified}");
assert!(s.contains("x") && s.contains("y"), "simplified: {s}");
}