use symplex::prelude::*;
use symplex::rsolve::{rsolve_first_order, rsolve_linear};
fn show_solution(label: &str, sol: &LinearSolution) {
match sol {
LinearSolution::Unique(pairs) => {
let parts: Vec<String> = pairs
.iter()
.map(|(v, val)| format!("{v} = {val}"))
.collect();
println!("{label}: unique {{ {} }}", parts.join(", "));
}
LinearSolution::Parametric { solution, free } => {
let parts: Vec<String> = solution
.iter()
.map(|(v, val)| format!("{v} = {val}"))
.collect();
let frees: Vec<String> = free.iter().map(|f| f.to_string()).collect();
println!(
"{label}: parametric {{ {} }} free: {}",
parts.join(", "),
frees.join(", ")
);
}
LinearSolution::Inconsistent => println!("{label}: inconsistent (no solution)"),
}
}
#[allow(clippy::eq_op, clippy::erasing_op)]
fn main() {
println!("=== Solving: Systems, General Solutions, IVPs, Recurrences ===\n");
let ctx = Context::new();
symplex::syms!(ctx; x, y, z, a, b, n, t);
println!("--- Ex::solve semantics ---");
let half = ctx.rational(1, 2);
println!(
"sin(x) = 1/2 → {:?}",
(&x.sin() - &half).solve(&x).map(strs)
);
println!(
"x² + 1 = 0 → {:?}",
(&x.powi(2) + 1).solve(&x).map(strs)
);
for (label, eq) in [
("x − x = 0", &x - &x),
("0·x + 1 = 0", &x * 0 + 1),
("sin(x) = 2", &x.sin() - 2),
("exp(x) = −1", &x.exp() + 1),
("|x| = −1", &x.abs() + 1),
] {
match eq.solve(&x) {
Ok(v) => println!("{label:<14} → Ok({:?})", strs(v)),
Err(SymplexError::InfiniteSolutions { .. }) => {
println!("{label:<14} → Err(InfiniteSolutions): identity, every x works")
}
Err(SymplexError::NoSolution { reason, .. }) => {
println!("{label:<14} → Err(NoSolution): {reason}")
}
Err(e) => println!("{label:<14} → Err({e})"),
}
}
println!("\n--- solve_general ---");
let fam = (&x.sin() - &half).solve_general(&x).unwrap();
println!(
"sin(x) = 1/2: {} with {} ∈ ℤ",
strs(fam.solutions.clone()).join(", "),
fam.parameters[0]
);
println!(" instance(k = 1): {}", strs(fam.instance(1)).join(", "));
let fam = (&(&x * 2).cos() - 1).solve_general(&x).unwrap();
println!("cos(2x) = 1: {}", strs(fam.solutions).join(", "));
let fam = (&x.tan() - 1).solve_general(&x).unwrap();
println!("tan(x) = 1: {}", strs(fam.solutions).join(", "));
let fam = (&x.powi(2) - 4).solve_general(&x).unwrap();
println!(
"x² = 4: {} (non-periodic: {} parameters)",
strs(fam.solutions).join(", "),
fam.parameters.len()
);
println!("\n--- linsolve ---");
let vars3 = [x.clone(), y.clone(), z.clone()];
let sol = linsolve(
&[&x + &y + &z - 6, &x - &y + 2 * &z - 5, &x * 2 + &y - &z - 1],
&vars3,
)
.unwrap();
show_solution("3×3 determined ", &sol);
let sol = linsolve(&[&x + &y + &z - 6, &x - &y - 2], &vars3).unwrap();
show_solution("2 eqs, 3 unknowns", &sol);
if let Some(v) = sol.get(&x) {
println!(" sol.get(x) = {v}");
}
let sol = linsolve(&[&x + &y - 1, &x + &y - 2], &[x.clone(), y.clone()]).unwrap();
show_solution("contradictory ", &sol);
let sol = linsolve(
&[eq!(ctx, a * x + y = 1), eq!(ctx, x - y = b)],
&[x.clone(), y.clone()],
)
.unwrap();
show_solution("symbolic coeffs ", &sol);
let am = matrix![ctx, [1, 2, 3], [4, 5, 6], [7, 8, 9]];
let bm = Matrix::col_vector(vec![ctx.int(6), ctx.int(15), ctx.int(24)]);
show_solution("singular A·x = b", &linsolve_matrix(&am, &bm).unwrap());
show_solution(
"ctx.solve_system",
&ctx.solve_system(&[&x + &y - 3, &x - &y - 1], &[x.clone(), y.clone()])
.unwrap(),
);
println!("\n--- polysys::solve_system_ex ---");
let vars2 = [x.clone(), y.clone()];
let sols =
symplex::polysys::solve_system_ex(&[&x.powi(2) + &y.powi(2) - 1, &x - &y], &vars2).unwrap();
for s in &sols {
println!("circle ∩ line: x = {}, y = {}", s[0], s[1]);
}
let sols =
symplex::polysys::solve_system_ex(&[&x.powi(2) + &y.powi(2) - 1, &x.powi(2) - &y], &vars2)
.unwrap();
println!(
"circle ∩ parabola: {} algebraic solutions, e.g. y = {}",
sols.len(),
sols[0][1]
);
match symplex::polysys::solve_system_ex(&[&x + &y - 1], &vars2) {
Err(SymplexError::InfiniteSolutions { reason, .. }) => {
println!("positive-dimensional → Err(InfiniteSolutions): {reason}")
}
other => println!("unexpected {other:?}"),
}
println!("\n--- solve_numeric_system ---");
let f1 = &x.powi(2) + &y.powi(2) - 4;
let f2 = &x.exp() + &y - 1;
let root = solve_numeric_system(&[f1.clone(), f2.clone()], &vars2, &[1.0, -1.0]).unwrap();
println!(
"x² + y² = 4, eˣ + y = 1 → x ≈ {:.10}, y ≈ {:.10}",
root[0], root[1]
);
let residual = f1.eval_f64_with(&[(&x, root[0]), (&y, root[1])]).unwrap();
println!(" residual of first equation: {residual:.2e}");
println!("\n--- ODEs and IVPs ---");
let yf = ctx.symbol("yf");
let d = |k: usize| {
let mut e = yf.clone();
for _ in 0..k {
e = e.formal_diff(&x);
}
e
};
let zero = ctx.int(0);
let at0 = |order: usize, value: i64| InitialCondition {
order,
x: zero.clone(),
value: ctx.int(value),
};
let ode = &d(2) + &yf; let sol = ode.solve_ode_ivp(&yf, &x, &[at0(0, 0), at0(1, 1)]).unwrap();
println!(
"y'' + y = 0, y(0)=0, y'(0)=1 → y = {}",
sol.simplify()
);
let ode = &d(1) + &yf * 2; let sol = ode.solve_ode_ivp(&yf, &x, &[at0(0, 3)]).unwrap();
println!(
"y' + 2y = 0, y(0)=3 → y = {}",
sol.simplify()
);
let ode = &d(2) - &d(1) * 3 + &yf * 2 - &x.exp() * 4;
println!("y'' − 3y' + 2y = 4eˣ [{:?}]", ode.classify_ode(&yf, &x));
println!(" general: y = {}", ode.solve_ode(&yf, &x));
let ode3 = &d(3) - &d(1); println!("y''' − y' = 0 [{:?}]", ode3.classify_ode(&yf, &x));
println!(" general: y = {}", ode3.solve_ode(&yf, &x));
let sol = ode3
.solve_ode_ivp(&yf, &x, &[at0(0, 0), at0(1, 1), at0(2, 0)])
.unwrap();
println!(" y(0)=0, y'(0)=1, y''(0)=0: y = {}", sol.simplify());
let clairaut = &yf - &x * &d(1) - &d(1).powi(2); println!(
"y = x·y' + (y')² [{:?}] → y = {}",
clairaut.classify_ode(&yf, &x),
clairaut.solve_ode(&yf, &x)
);
let riccati = &d(1) - &yf.powi(2) + &(&ctx.int(2) / &x.powi(2)); println!(
"y' = y² − 2/x², particular 1/x → y = {}",
riccati.solve_riccati(&yf, &x, &(&ctx.int(1) / &x)).unwrap()
);
let a_mat = matrix![ctx, [0, 1], [-1, 0]];
let sys = symplex::ode::solve_ode_system_ivp(&a_mat, &t, &[ctx.int(1), ctx.int(0)]).unwrap();
println!(
"x' = [[0,1],[−1,0]] x, x(0) = (1, 0) → x(t) = ({}, {})",
sys[0].simplify(),
sys[1].simplify()
);
println!("\n--- rsolve ---");
let fib = rsolve_linear(
&[ctx.int(-1), ctx.int(-1), ctx.int(1)],
None,
&n,
&[ctx.int(0), ctx.int(1)],
)
.unwrap();
println!("Fibonacci a(n+2) = a(n+1) + a(n): a(n) = {fib}");
println!(" a(10) = {}", fib.subs_i64(&n, 10).eval().simplify());
let hanoi = rsolve_linear(
&[ctx.int(-2), ctx.int(1)],
Some(&ctx.int(1)),
&n,
&[ctx.int(0)],
)
.unwrap();
println!("Towers of Hanoi a(n+1) = 2a(n) + 1: a(n) = {hanoi}");
let general = rsolve_linear(&[ctx.int(6), ctx.int(-5), ctx.int(1)], None, &n, &[]).unwrap();
println!("a(n+2) − 5a(n+1) + 6a(n) = 0: a(n) = {general}");
let tri = rsolve_linear(&[ctx.int(-1), ctx.int(1)], Some(&n), &n, &[ctx.int(0)]).unwrap();
println!(
"a(n+1) = a(n) + n, a(0) = 0: a(n) = {}",
tri.expand()
);
let fact = rsolve_first_order(&(&n + 1), &ctx.int(0), &n, Some(&ctx.int(1))).unwrap();
println!("a(n+1) = (n+1)·a(n), a(0) = 1: a(n) = {fact}");
println!("\n--- Inequalities ---");
println!("|x − 1| < 2 → {}", (&(&x - 1).abs() - 2).solve_lt(&x));
println!("|x| ≥ 3 → {}", (&x.abs() - 3).solve_ge(&x));
println!("x² − 4 > 0 → {}", (&x.powi(2) - 4).solve_gt(&x));
println!("\n✓ Done!");
}
fn strs(v: Vec<Ex>) -> Vec<String> {
v.iter().map(|e| e.to_string()).collect()
}