//! Cross-module integration tests for SymClaw. 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, linalg, number_theory, ode, parser, pattern, poly, precision, proof, series, simplify, streaming, tensor, transforms, units, }; fn parse(s: &str) -> Arc { parser::parse(s).unwrap() } // ── 1. Parse → Simplify → Differentiate → Integrate roundtrip ── #[test] fn parse_simplify_differentiate_integrate_roundtrip() { let expr = parse("x^3 + 2*x"); let simplified = simplify::simplify(&expr); let deriv = differentiate::differentiate(&simplified, Symbol::new("x")); // d/dx(x^3 + 2x) = 3x^2 + 2 let integral = integrate::integrate(&deriv, Symbol::new("x")); assert!(integral.is_some()); } // ── 2. Parse → to_polynomial → back to expr ──────────────────── #[test] fn parse_to_polynomial_roundtrip() { let expr = parse("x^2 + 2*x + 1"); let p = poly::Polynomial::from_expr(&expr).expect("should convert to polynomial"); let back = p.to_expr(); // Simplify both and compare let s1 = simplify::simplify(&expr); let s2 = simplify::simplify(&Arc::new(back)); assert_eq!(format!("{s1}"), format!("{s2}")); } // ── 3. Parse → limit → verify ────────────────────────────────── #[test] fn limit_sinx_over_x() { 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.contains("1"), "expected 1, got {s}"); } other => panic!("expected finite limit, got {other:?}"), } } // ── 4. Laplace transform ──────────────────────────────────────── #[test] fn laplace_of_exp() { // L{e^(a*t)} = 1/(s-a) let expr = parse("exp(a*t)"); let result = transforms::laplace(&expr, "t", "s"); assert!(result.is_some(), "Laplace of exp(a*t) should exist"); } // ── 5. Codegen to all 7 languages ────────────────────────────── #[test] fn codegen_all_languages() { let expr = parse("x^2 + sin(x)"); let langs = [ codegen::Language::Python, codegen::Language::C, codegen::Language::Rust, codegen::Language::Julia, codegen::Language::JavaScript, codegen::Language::GLSL, codegen::Language::WGSL, ]; for lang in &langs { let opts = codegen::CodegenOptions::new(*lang); let code = codegen::generate(&expr, &opts); assert!( !code.is_empty(), "codegen for {lang:?} produced empty output" ); } } // ── 6. Proof trace ────────────────────────────────────────────── #[test] fn proof_trace_simplification() { let expr = parse("x + 0"); let trace = proof::trace_simplify(&expr); assert!(!trace.original.is_empty()); assert!(!trace.result.is_empty()); } // ── 7. Units check correct ───────────────────────────────────── #[test] fn units_check_correct() { let expr = parse("F - m*a"); let ctx = units::parse_dim_context("F:force,m:mass,a:acceleration").unwrap(); let result = units::infer_dimension(&expr, &ctx); assert!(result.is_ok(), "F - m*a should be dimensionally consistent"); } // ── 8. Units check incorrect ──────────────────────────────────── #[test] fn units_check_incorrect() { let expr = parse("F + m"); let ctx = units::parse_dim_context("F:force,m:mass").unwrap(); let result = units::infer_dimension(&expr, &ctx); assert!( result.is_err(), "F + m should be dimensionally inconsistent" ); } // ── 9. Pattern match → verify ─────────────────────────────────── #[test] fn pattern_match_trig_identity() { let expr = parse("sin(x)^2 + cos(x)^2"); let pat = pattern::parse_pattern("sin(?x)^2 + cos(?x)^2").unwrap(); let bindings = pattern::match_pattern(&pat, &expr); assert!(bindings.is_some(), "should match trig identity pattern"); let b = bindings.unwrap(); assert!(b.contains_key("x"), "should capture x"); } // ── 10. Save → load roundtrip ─────────────────────────────────── #[test] fn save_load_roundtrip() { let expr = parse("x^2 + sin(x)"); let path = "/tmp/symclaw_test_integration.bin"; let config = streaming::StreamConfig::default(); streaming::save_to_file(&expr, path, &config).unwrap(); let loaded = streaming::load_from_file(path).unwrap(); assert_eq!(format!("{expr}"), format!("{}", Arc::new(loaded))); let _ = std::fs::remove_file(path); } // ── 11. Matrix det ────────────────────────────────────────────── #[test] fn matrix_determinant() { let mat = linalg::from_rows(vec![ vec![Expr::int(1), Expr::int(2)], vec![Expr::int(3), Expr::int(4)], ]); let det = linalg::determinant(&mat).unwrap(); let simplified = simplify::simplify(&det); // det = 1*4 - 2*3 = -2 let s = format!("{simplified}"); assert!(s == "-2" || s == "(-2)", "expected -2, got {s}"); } // ── 12. Number theory: factorize → verify product ─────────────── #[test] fn factorize_verify_product() { let n = 360u64; let factors = number_theory::factorize(n); let product: u64 = factors.iter().map(|&(p, e)| p.pow(e)).product(); assert_eq!(product, n); } // ── 13. Number theory: is_prime ───────────────────────────────── #[test] fn is_prime_basic() { assert!(number_theory::is_prime(97)); assert!(!number_theory::is_prime(100)); assert!(number_theory::is_prime(2)); assert!(!number_theory::is_prime(1)); } // ── 14. Tensor parse → contracted pairs ───────────────────────── #[test] fn tensor_contract() { let te = tensor::parse_tensor_expr("T^mu_nu * S^nu").unwrap(); let contracted = tensor::contracted_pairs(&te); assert!(contracted.contains(&"nu".to_owned())); } // ── 15. BigFloat pi computation ───────────────────────────────── #[test] fn bigfloat_pi() { let pi = precision::BigFloat::pi(50); let val = pi.to_f64(); assert!((val - std::f64::consts::PI).abs() < 1e-10); } // ── 16. ErrorFloat propagation ────────────────────────────────── #[test] fn errorfloat_propagation() { let a = precision::ErrorFloat::new(10.0, 0.1); let b = precision::ErrorFloat::new(5.0, 0.2); let c = a.add(&b); assert!((c.value - 15.0).abs() < 1e-10); assert!(c.uncertainty > 0.0); } // ── 17. Taylor → eval at point ────────────────────────────────── #[test] fn taylor_exp_at_zero() { let expr = parse("exp(x)"); let point = parse("0"); let expansion = series::taylor(&expr, Symbol::new("x"), &point, 5); // Evaluate at x=0.1, compare with e^0.1 let mut vars = HashMap::new(); vars.insert(Symbol::new("x"), 0.1); let val = eval::eval(&expansion, &vars).unwrap(); assert!((val - 0.1_f64.exp()).abs() < 1e-4); } // ── 18. ODE solve ─────────────────────────────────────────────── #[test] fn ode_first_order_linear() { // y' - y = 0 → y = C*exp(x) // Construct Derivative node manually since parser doesn't handle y' let y_prime = Arc::new(Expr::Derivative { expr: Arc::new(Expr::Sym(Symbol::new("y"))), var: Symbol::new("x"), order: 1, }); let y = Arc::new(Expr::Sym(Symbol::new("y"))); let expr = Arc::new(Expr::Add(vec![y_prime, Arc::new(Expr::Neg(y))])); let sol = ode::solve_ode1(&expr, Symbol::new("y"), Symbol::new("x")); assert!(sol.is_some(), "should solve y' - y = 0"); } // ── 19. GCD number theory ─────────────────────────────────────── #[test] fn gcd_basic() { assert_eq!(number_theory::gcd(12, 18), 6); assert_eq!(number_theory::gcd(100, 75), 25); } // ── 20. Limit at infinity ─────────────────────────────────────── #[test] fn limit_at_infinity() { let expr = parse("1/x"); let result = limits::limit_inf(&expr, "x"); match result { limits::LimitResult::Finite(e) => { let s = format!("{e}"); assert!(s == "0" || s.contains("0"), "expected 0, got {s}"); } other => panic!("expected finite limit 0, got {other:?}"), } } // ── 21. Differentiate chain rule ──────────────────────────────── #[test] fn differentiate_chain_rule() { let expr = parse("sin(x^2)"); let deriv = differentiate::differentiate(&expr, Symbol::new("x")); let simplified = simplify::simplify(&deriv); // Should contain cos(x^2) and 2*x let s = format!("{simplified}"); assert!( s.contains("cos") || s.contains("2"), "chain rule result: {s}" ); } // ── 22. Polynomial GCD ───────────────────────────────────────── #[test] fn polynomial_from_expr_and_back() { let expr = parse("x^3 + 3*x^2 + 3*x + 1"); let p = poly::Polynomial::from_expr(&expr).unwrap(); assert!(p.degree() >= 3); } // ── 23. Euler totient ─────────────────────────────────────────── #[test] fn euler_totient() { assert_eq!(number_theory::euler_totient(12), 4); assert_eq!(number_theory::euler_totient(1), 1); }