@@ -8,7 +8,7 @@ use std::f64::consts::{E, PI}; |
| 8 | 8 | |
| 9 | 9 | use crate::error::{CasError, Result}; |
| 10 | 10 | use crate::expr::{Expr, Rational, Sign, Symbol}; |
| 11 | | -use crate::symbolic::{Differentiator, Integrator, Limits, Simplifier, Solver}; |
| 11 | +use crate::symbolic::{Differentiator, Factorer, Integrator, Limits, Simplifier, Solver}; |
| 12 | 12 | |
| 13 | 13 | /// Variable bindings for evaluation |
| 14 | 14 | pub type Environment = HashMap<String, Expr>; |
@@ -571,8 +571,31 @@ impl Evaluator { |
| 571 | 571 | } |
| 572 | 572 | |
| 573 | 573 | ("solve", 2, _) => { |
| 574 | + // solve([eq1, eq2], [x, y]) — system of equations |
| 575 | + if let (Expr::Vector(equations), Expr::Vector(vars)) = (&args[0], &args[1]) { |
| 576 | + let var_symbols: std::result::Result<Vec<Symbol>, _> = vars |
| 577 | + .iter() |
| 578 | + .map(|v| match v { |
| 579 | + Expr::Symbol(s) => Ok(s.clone()), |
| 580 | + _ => Err(CasError::Type( |
| 581 | + "solve system requires variables as second argument".to_string(), |
| 582 | + )), |
| 583 | + }) |
| 584 | + .collect(); |
| 585 | + let var_symbols = var_symbols?; |
| 586 | + let solutions = Solver::solve_system(equations, &var_symbols)?; |
| 587 | + // Return as vector of equations: [x = val1, y = val2] |
| 588 | + let result: Vec<Expr> = solutions |
| 589 | + .into_iter() |
| 590 | + .map(|(var, val)| Expr::Equation( |
| 591 | + Box::new(Expr::Symbol(var)), |
| 592 | + Box::new(val), |
| 593 | + )) |
| 594 | + .collect(); |
| 595 | + Ok(Expr::Vector(result)) |
| 596 | + } |
| 574 | 597 | // solve(expr, var) or solve(equation, var) |
| 575 | | - if let Expr::Symbol(var) = &args[1] { |
| 598 | + else if let Expr::Symbol(var) = &args[1] { |
| 576 | 599 | let solutions = Solver::solve(&args[0], var)?; |
| 577 | 600 | if solutions.len() == 1 { |
| 578 | 601 | Ok(solutions.into_iter().next().unwrap()) |
@@ -611,9 +634,19 @@ impl Evaluator { |
| 611 | 634 | ("expand", 1, _) => Ok(Simplifier::simplify(&Simplifier::expand(&args[0]))), |
| 612 | 635 | |
| 613 | 636 | ("factor", 1, _) => { |
| 614 | | - // Basic factoring - just return simplified for now |
| 615 | | - // Full factoring is complex, can add later |
| 616 | | - Ok(Simplifier::simplify(&args[0])) |
| 637 | + let var = Self::infer_primary_var(&args[0]) |
| 638 | + .unwrap_or_else(|| Symbol::new("x")); |
| 639 | + Ok(Factorer::factor(&args[0], &var)) |
| 640 | + } |
| 641 | + |
| 642 | + ("factor", 2, _) => { |
| 643 | + if let Expr::Symbol(var) = &args[1] { |
| 644 | + Ok(Factorer::factor(&args[0], var)) |
| 645 | + } else { |
| 646 | + Err(CasError::Type( |
| 647 | + "factor requires variable as second argument".to_string(), |
| 648 | + )) |
| 649 | + } |
| 617 | 650 | } |
| 618 | 651 | |
| 619 | 652 | ("substitute", 3, _) | ("subs", 3, _) => { |
@@ -1195,6 +1228,18 @@ impl Evaluator { |
| 1195 | 1228 | } |
| 1196 | 1229 | } |
| 1197 | 1230 | |
| 1231 | + fn infer_primary_var(expr: &Expr) -> Option<crate::expr::Symbol> { |
| 1232 | + let mut vars = BTreeSet::new(); |
| 1233 | + Self::collect_symbols(expr, &mut vars); |
| 1234 | + vars.remove("pi"); |
| 1235 | + vars.remove("e"); |
| 1236 | + if vars.len() == 1 { |
| 1237 | + vars.into_iter().next().map(crate::expr::Symbol::new) |
| 1238 | + } else { |
| 1239 | + None |
| 1240 | + } |
| 1241 | + } |
| 1242 | + |
| 1198 | 1243 | fn collect_symbols(expr: &Expr, out: &mut BTreeSet<String>) { |
| 1199 | 1244 | match expr { |
| 1200 | 1245 | Expr::Symbol(s) => { |