use symplex::prelude::*;
#[test]
fn i_squared_is_neg_one() {
let ctx = Context::new();
let i = ctx.i_unit();
let result = i.powi(2);
assert_eq!(format!("{result}"), "-1");
}
#[test]
fn i_cubed_is_neg_i() {
let ctx = Context::new();
let i = ctx.i_unit();
let result = i.powi(3);
assert_eq!(format!("{result}"), "-I");
}
#[test]
fn i_fourth_is_one() {
let ctx = Context::new();
let i = ctx.i_unit();
let result = i.powi(4);
assert_eq!(format!("{result}"), "1");
}
#[test]
fn i_to_100() {
let ctx = Context::new();
let i = ctx.i_unit();
let result = i.powi(100);
assert_eq!(format!("{result}"), "1"); }
#[test]
fn i_to_neg_one() {
let ctx = Context::new();
let i = ctx.i_unit();
let result = i.powi(-1);
assert_eq!(format!("{result}"), "-I"); }
#[test]
fn i_to_neg_two() {
let ctx = Context::new();
let i = ctx.i_unit();
let result = i.powi(-2);
assert_eq!(format!("{result}"), "-1"); }
#[test]
fn one_plus_i_squared() {
let ctx = Context::new();
let i = ctx.i_unit();
let expr = (&ctx.int(1) + &i).powi(2).expand();
let s = format!("{expr}");
assert_eq!(s, "2*I");
}
#[test]
fn i_times_i() {
let ctx = Context::new();
let i = ctx.i_unit();
let result = &i * &i;
assert_eq!(format!("{result}"), "-1");
}
#[test]
fn complex_addition() {
let ctx = Context::new();
let i = ctx.i_unit();
let z1 = &ctx.int(2) + &(&ctx.int(3) * &i);
let z2 = &ctx.int(4) + &(&ctx.int(5) * &i);
let sum = &z1 + &z2;
let s = format!("{sum}");
assert_eq!(s, "8*I + 6");
}
#[test]
fn diff_complex_expression() {
let ctx = Context::new();
let x = ctx.symbol("x");
let i = ctx.i_unit();
let expr = &i * &x.powi(2);
let result = expr.diff(&x);
let s = format!("{result}");
assert!(
s.contains("I") && s.contains("x"),
"d/dx(i*x²) should be 2*i*x, got: {s}"
);
}
#[test]
fn integrate_complex_expression() {
let ctx = Context::new();
let x = ctx.symbol("x");
let i = ctx.i_unit();
let expr = &i * &x;
let result = expr.integrate(&x);
let s = format!("{result}");
assert!(
s.contains("I") && s.contains("x"),
"∫ i*x dx should involve I and x, got: {s}"
);
}
#[test]
fn i_is_imaginary() {
let ctx = Context::new();
let i = ctx.i_unit();
assert_eq!(i.query(Props::IMAGINARY), Some(true));
assert_eq!(i.query(Props::REAL), Some(false));
assert_eq!(i.query(Props::COMPLEX), Some(true));
}
#[test]
fn i_squared_is_real() {
let ctx = Context::new();
let i = ctx.i_unit();
let i2 = i.powi(2);
assert_eq!(i2.is_real(), Some(true));
assert_eq!(i2.is_negative(), Some(true));
}
#[test]
fn solve_x2_plus_1() {
let ctx = Context::new();
let x = ctx.symbol("x");
let eq = &x.powi(2) + 1;
let roots = eq.solve_or_empty(&x);
if roots.len() == 2 {
let strs: Vec<String> = roots.iter().map(|r| format!("{r}")).collect();
let joined = strs.join(", ");
assert!(joined.contains("I"), "roots should contain I: {joined}");
} else {
assert_eq!(roots.len(), 0, "expected 0 (unsupported) or 2 roots");
}
}
#[test]
fn solve_x2_plus_4() {
let ctx = Context::new();
let x = ctx.symbol("x");
let eq = &x.powi(2) + 4;
let roots = eq.solve_or_empty(&x);
if roots.len() == 2 {
let strs: Vec<String> = roots.iter().map(|r| format!("{r}")).collect();
let joined = strs.join(", ");
assert!(joined.contains("I"), "roots should contain I: {joined}");
} else {
assert_eq!(roots.len(), 0, "expected 0 (unsupported) or 2 roots");
}
}
#[test]
fn solve_x2_plus_2x_plus_5() {
let ctx = Context::new();
let x = ctx.symbol("x");
let eq = &x.powi(2) + &(&x * 2) + 5;
let roots = eq.solve_or_empty(&x);
if roots.len() == 2 {
let strs: Vec<String> = roots.iter().map(|r| format!("{r}")).collect();
let joined = strs.join(", ");
assert!(joined.contains("I"), "roots should contain I: {joined}");
} else {
assert_eq!(roots.len(), 0, "expected 0 (unsupported) or 2 roots");
}
}
#[test]
fn euler_exp_i_pi() {
let ctx = Context::new();
let i = ctx.i_unit();
let expr = (&i * &ctx.pi()).exp().eval();
let s = format!("{expr}");
assert!(
s == "-1" || s.contains("exp") && s.contains("I"),
"exp(iπ) should be -1 or unevaluated exp(…I): {s}"
);
}
#[test]
fn euler_exp_i_pi_over_2() {
let ctx = Context::new();
let i = ctx.i_unit();
let angle = &ctx.rational(1, 2) * &ctx.pi();
let expr = (&i * &angle).exp().eval();
let s = format!("{expr}");
assert!(
s == "I" || s.contains("exp") && s.contains("I"),
"exp(iπ/2) should be I or unevaluated exp(…I): {s}"
);
}
#[test]
fn euler_exp_i_pi_plus_1_is_zero() {
let ctx = Context::new();
let i = ctx.i_unit();
let expr = &(&i * &ctx.pi()).exp() + 1;
let evald = expr.eval();
let s = format!("{evald}");
assert!(
s == "0" || s.contains("exp"),
"exp(iπ)+1 should be 0 or contain unevaluated exp: {s}"
);
}
#[test]
fn global_i_unit() {
let ctx = Context::new();
let i = ctx.i_unit();
assert_eq!(format!("{i}"), "I");
assert_eq!(i.is_imaginary(), Some(true));
}
#[test]
fn global_pi() {
let ctx = Context::new();
let pi = ctx.pi();
assert_eq!(format!("{pi}"), "pi");
}
#[test]
fn global_e() {
let ctx = Context::new();
let e = ctx.e();
assert_eq!(format!("{e}"), "E");
}
#[test]
fn is_imaginary_convenience() {
let ctx = Context::new();
let i = ctx.i_unit();
assert_eq!(i.is_imaginary(), Some(true));
let x = ctx.symbol("x");
assert_eq!(x.is_imaginary(), None);
}
#[test]
fn is_nonnegative_convenience() {
let ctx = Context::new();
let two = ctx.int(2);
assert_eq!(two.is_nonnegative(), Some(true));
let neg = ctx.int(-3);
assert_eq!(neg.is_nonnegative(), Some(false));
}