191 lines
6.7 KiB
Rust
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}");
|
|
}
|