use symplex::prelude::*;
#[test]
fn solve_quintic_returns_rootof() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(5) - &x - 1;
let roots = poly.solve_or_empty(&x);
assert!(
!roots.is_empty(),
"quintic x⁵ − x − 1 should return RootOf objects, got empty"
);
for root in &roots {
let s = format!("{root}");
assert!(s.contains("RootOf"), "expected RootOf in display, got: {s}");
}
}
#[test]
fn solve_quintic_returns_five_roots() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(5) - &x - 1;
let roots = poly.solve_or_empty(&x);
assert_eq!(
roots.len(),
5,
"degree-5 polynomial should yield 5 RootOf objects, got {}",
roots.len()
);
}
#[test]
fn solve_sextic_returns_rootof() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(6) + &x + 1;
let roots = poly.solve_or_empty(&x);
assert!(
!roots.is_empty(),
"sextic x⁶ + x + 1 should return RootOf objects"
);
assert_eq!(
roots.len(),
6,
"degree-6 polynomial should yield 6 RootOf objects, got {}",
roots.len()
);
}
#[test]
fn solve_quintic_with_rational_root_mixed() {
let ctx = Context::new();
let x = ctx.symbol("x");
let factor1 = &x - 1;
let factor2 = &x.powi(5) - &x - 1;
let poly = &factor1 * &factor2;
let roots = poly.solve_or_empty(&x);
let strs: Vec<String> = roots.iter().map(|r| format!("{r}")).collect();
let has_one = strs.iter().any(|s| s == "1");
let has_rootof = strs.iter().any(|s| s.contains("RootOf"));
assert!(has_one, "should find rational root x = 1 among: {strs:?}");
assert!(
has_rootof,
"should also have RootOf entries for the quintic factor: {strs:?}"
);
}
#[test]
fn solve_degree_7_returns_rootof() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(7) - &(&x * 2) - 5;
let roots = poly.solve_or_empty(&x);
assert_eq!(
roots.len(),
7,
"degree-7 polynomial should yield 7 RootOf objects, got {}",
roots.len()
);
}
#[test]
fn rootof_eval_f64_quintic() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(5) - &x - 1;
let roots = poly.solve_or_empty(&x);
assert!(!roots.is_empty());
let mut real_count = 0;
for root in &roots {
if let Ok(v) = root.eval_f64() {
real_count += 1;
let residual = poly.subs(&x, root).eval();
if let Ok(r) = residual.eval_f64() {
assert!(
r.abs() < 1e-6,
"RootOf should satisfy the equation, residual = {r}"
);
}
assert!(
(v - 1.1673).abs() < 0.01,
"real root of x⁵−x−1 ≈ 1.1673, got {v}"
);
}
}
assert!(
real_count >= 1,
"at least one real root should be numerically evaluable"
);
}
#[test]
fn rootof_eval_decimal_quintic() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(5) - &x - 1;
let roots = poly.solve_or_empty(&x);
let mut found_real = false;
for root in &roots {
if let Ok(dec) = root.eval_decimal(15)
&& dec.starts_with("1.167")
{
found_real = true;
}
}
assert!(
found_real,
"should find the real root ≈ 1.167 among the RootOf objects"
);
}
#[test]
fn rootof_eval_f64_degree7() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(7) - &(&x * 2) - 5;
let roots = poly.solve_or_empty(&x);
let mut found_real = false;
for root in &roots {
if let Ok(v) = root.eval_f64() {
found_real = true;
let residual = poly.subs(&x, root).eval();
if let Ok(r) = residual.eval_f64() {
assert!(
r.abs() < 1e-6,
"residual should be ~0, got {r} for root value {v}"
);
}
}
}
assert!(
found_real,
"degree-7 polynomial should have at least one evaluable real root"
);
}
#[test]
fn rootof_complex_roots_are_unevaluable() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(5) - &x - 1;
let roots = poly.solve_or_empty(&x);
let mut eval_ok = 0;
let mut eval_err = 0;
for root in &roots {
match root.eval_f64() {
Ok(_) => eval_ok += 1,
Err(_) => eval_err += 1,
}
}
assert_eq!(eval_ok, 1, "exactly 1 real root should be evaluable");
assert_eq!(eval_err, 4, "4 complex roots should fail eval_f64");
}
#[test]
fn solve_api_returns_ok_for_quintic() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(5) - &x - 1;
let result = poly.solve(&x);
assert!(
result.is_ok(),
"solve() should return Ok for polynomial expressions, got: {result:?}"
);
let roots = result.unwrap();
assert_eq!(roots.len(), 5);
}
#[test]
fn solve_quadratic_still_works() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(2) - 1;
let roots = poly.solve_or_empty(&x);
assert_eq!(roots.len(), 2, "x²−1 should have 2 roots");
let mut strs: Vec<String> = roots.iter().map(|r| format!("{r}")).collect();
strs.sort();
assert_eq!(strs, vec!["-1", "1"]);
}
#[test]
fn solve_quartic_still_works() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &(&(&(&x - 1) * &(&x - 2)) * &(&x - 3)) * &(&x - 4);
let roots = poly.solve_or_empty(&x);
assert!(
roots.len() >= 4,
"(x-1)(x-2)(x-3)(x-4) should have 4 roots, got {}",
roots.len()
);
}
#[test]
fn rootof_eval_multiple_real_roots() {
let ctx = Context::new();
let x = ctx.symbol("x");
let poly = &x.powi(5) - &(&x.powi(3) * 5) + &(&x * 4);
let roots = poly.solve_or_empty(&x);
assert!(
roots.len() >= 5,
"x⁵ − 5x³ + 4x should have 5 rational roots, got {}",
roots.len()
);
for root in &roots {
let s = format!("{root}");
assert!(
!s.contains("RootOf"),
"fully factorable polynomial should not produce RootOf: {s}"
);
}
}