use symplex::prelude::*;
#[test]
fn trigsimp_pythagorean() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = &x.sin().powi(2) + &x.cos().powi(2);
assert_eq!(format!("{}", e.simplify_trig()), "1");
}
#[test]
fn trigsimp_in_larger_expr() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = &x.sin().powi(2) + &x.cos().powi(2) + &x;
let result = e.simplify_trig();
let s = format!("{result}");
assert_eq!(s, "x + 1");
}
#[test]
fn trigsimp_pythagorean_plus_number() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = &x.sin().powi(2) + &x.cos().powi(2) + 5;
assert_eq!(format!("{}", e.simplify_trig()), "6");
}
#[test]
fn trigsimp_leaves_bare_trig_alone() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = x.sin();
let result = e.simplify_trig();
let s = format!("{result}");
assert_eq!(s, "sin(x)");
}
#[test]
fn trigsimp_preserves_numeric_value() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = &x.sin().powi(2) + &x.cos().powi(2) + &x;
let val_before = e.subs(&x, &ctx.rational(7, 10)).eval_f64().unwrap();
let result = e.simplify_trig();
let val_after = result.subs(&x, &ctx.rational(7, 10)).eval_f64().unwrap();
assert!(
(val_before - val_after).abs() < 1e-10,
"trigsimp should preserve value: {val_before} vs {val_after}"
);
}
#[test]
fn powsimp_symbolic_exponents() {
let ctx = Context::new();
symplex::syms!(ctx; x, a, b);
let e = &x.pow(&a) * &x.pow(&b);
let result = e.simplify_powers();
let s = format!("{result}");
assert!(
s.contains("a + b") || s.contains("b + a"),
"powsimp should combine: {s}"
);
}
#[test]
fn powsimp_three_factors() {
let ctx = Context::new();
symplex::syms!(ctx; x, a, b, c);
let e = &(&x.pow(&a) * &x.pow(&b)) * &x.pow(&c);
let result = e.simplify_powers();
let s = format!("{result}");
assert!(
s.contains('a') && s.contains('b') && s.contains('c'),
"powsimp should combine all three: {s}"
);
let carets = s.matches('^').count();
assert_eq!(carets, 1, "should be a single power: {s}");
}
#[test]
fn powsimp_different_bases_untouched() {
let ctx = Context::new();
symplex::syms!(ctx; x, y, a, b);
let e = &x.pow(&a) * &y.pow(&b);
let result = e.simplify_powers();
let s = format!("{result}");
assert!(
s.contains("x^a") && s.contains("y^b"),
"different bases should be untouched: {s}"
);
}
#[test]
fn powsimp_atom_unchanged() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let result = x.simplify_powers();
assert_eq!(format!("{result}"), "x");
}
#[test]
fn rewrite_sin_as_exp() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = x.sin();
let result = e.rewrite_as_exp();
let s = format!("{result}");
assert!(
s.contains("exp") || s.contains("E"),
"rewrite should produce exponentials: {s}"
);
}
#[test]
fn rewrite_cos_as_exp() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = x.cos();
let result = e.rewrite_as_exp();
let s = format!("{result}");
assert!(
s.contains("exp") || s.contains("E"),
"rewrite should produce exponentials: {s}"
);
}
#[test]
fn rewrite_as_exp_preserves_value() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = x.sin();
let val = ctx.rational(7, 10);
let orig_f = e.subs(&x, &val).eval_f64().unwrap();
let rewritten = e.rewrite_as_exp();
let rw_f = rewritten.subs(&x, &val).eval_f64();
let rw_val: Result<(f64, f64), _> = rewritten.subs(&x, &val).eval_complex64();
if let Ok((re, im)) = rw_val {
assert!(
im.abs() < 1e-10,
"sin rewritten as exp should be real for real input: im={im}"
);
assert!(
(orig_f - re).abs() < 1e-10,
"rewrite should preserve value: {orig_f} vs {re}"
);
} else if let Ok(f) = rw_f {
assert!(
(orig_f - f).abs() < 1e-10,
"rewrite should preserve value: {orig_f} vs {f}"
);
}
}
#[test]
fn rewrite_exp_ix_as_trig() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let i = ctx.i_unit();
let expr = (&i * &x).exp();
let result = expr.rewrite_as_trig();
let s = format!("{result}");
assert!(
s.contains("cos") && s.contains("sin"),
"rewrite should produce trig: {s}"
);
}
#[test]
fn rewrite_as_trig_atom_unchanged() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let result = x.rewrite_as_trig();
assert_eq!(format!("{result}"), "x");
}
#[test]
fn rewrite_as_exp_atom_unchanged() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let result = x.rewrite_as_exp();
assert_eq!(format!("{result}"), "x");
}
#[test]
fn rewrite_roundtrip_numerical() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let e = x.sin();
let as_exp = e.rewrite_as_exp();
let back = as_exp.rewrite_as_trig().eval().simplify();
let val = ctx.rational(7, 10);
let orig_f = e.subs(&x, &val).eval_f64().unwrap();
let back_result: Result<(f64, f64), _> = back.subs(&x, &val).eval_complex64();
if let Ok((re, im)) = back_result {
assert!(
(orig_f - re).abs() < 1e-10,
"roundtrip should preserve value: orig={orig_f}, back_re={re}, back_im={im}"
);
}
}