use symplex::prelude::*;
fn assert_series_is(e: &Ex, x: &Ex, expected: &Ex, at_tenth: f64) {
let ctx = e.context();
let s = e.series(x, &ctx.int(0), 6);
assert!(
!s.has_unevaluated(),
"series({e}) came back unevaluated: {s}"
);
assert_eq!(
s.equals(expected),
Some(true),
"series({e}, x, 0, 6) = {s}, sympy says {expected}"
);
let v = s
.subs(x, &ctx.rational(1, 10))
.eval_f64()
.unwrap_or_else(|err| panic!("{s} at 1/10 did not evaluate: {err}"));
assert!(
(v - at_tenth).abs() <= 1e-12,
"series({e}) = {s} at 1/10 gives {v}, sympy says {at_tenth}"
);
}
#[test]
fn series_abs_x_squared() {
let ctx = Context::new();
let x = ctx.symbol("x");
assert_series_is(&x.powi(2).abs(), &x, &x.powi(2), 0.01);
}
#[test]
fn series_cos_abs_x_is_cos_series() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expected = x.powi(4) * ctx.rational(1, 24) - x.powi(2) * ctx.rational(1, 2) + ctx.int(1);
assert_series_is(&x.abs().cos(), &x, &expected, 0.9950041666666667);
}
#[test]
fn series_abs_atan_squared() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expected = x.powi(2) - x.powi(4) * ctx.rational(2, 3);
assert_series_is(&x.atan().powi(2).abs(), &x, &expected, 0.009933333333333334);
}
#[test]
fn series_abs_x_minus_one_at_zero() {
let ctx = Context::new();
let x = ctx.symbol("x");
let expected = ctx.int(1) - &x;
assert_series_is(&(&x - ctx.int(1)).abs(), &x, &expected, 0.9);
}
#[test]
fn series_abs_x_squared_via_even_power_of_abs() {
let ctx = Context::new();
let x = ctx.symbol("x");
assert_series_is(&x.abs().powi(2), &x, &x.powi(2), 0.01);
}
#[test]
fn series_abs_x_is_unevaluated() {
let ctx = Context::new();
let x = ctx.symbol("x");
let s = x.abs().series(&x, &ctx.int(0), 6);
assert!(
s.has_unevaluated(),
"series(|x|) must not be a polynomial: {s}"
);
assert!(!s.is_zero_structural(), "series(|x|) must not be 0");
}
#[test]
fn series_abs_sin_x_is_unevaluated() {
let ctx = Context::new();
let x = ctx.symbol("x");
let s = x.sin().abs().series(&x, &ctx.int(0), 6);
assert!(
s.has_unevaluated(),
"series(|sin x|) must not be a polynomial: {s}"
);
}
#[test]
fn series_exp_abs_x_is_unevaluated() {
let ctx = Context::new();
let x = ctx.symbol("x");
let s = x.abs().exp().series(&x, &ctx.int(0), 6);
assert!(
s.has_unevaluated(),
"series(exp|x|) must not be a polynomial: {s}"
);
assert!(
!s.equals(&ctx.int(1)).unwrap_or(false),
"series(exp|x|) must not be the constant 1"
);
}
#[test]
fn series_abs_x_at_infinity_is_one_sided() {
let ctx = Context::new();
let x = ctx.symbol("x");
let plus = x.abs().series(&x, &ctx.infinity(), 3);
assert_eq!(
plus.id(),
x.id(),
"series(|x|, x, oo) should be x; got {plus}"
);
let minus = x.abs().series(&x, &ctx.neg_infinity(), 3);
assert_eq!(
minus.id(),
(-&x).id(),
"series(|x|, x, -oo) should be -x; got {minus}"
);
}
#[test]
fn series_x_to_5_2_unevaluated_at_order_3() {
let ctx = Context::new();
let x = ctx.symbol("x");
let e = x.pow(&ctx.rational(5, 2));
let s = e.series(&x, &ctx.int(0), 3);
assert!(
!s.is_zero_structural(),
"series(x^(5/2), x, 0, 3) must not be 0"
);
assert!(
s.has_unevaluated(),
"series(x^(5/2), x, 0, 3) should be unevaluated: {s}"
);
}
#[test]
fn series_x_to_5_2_unevaluated_at_order_6() {
let ctx = Context::new();
let x = ctx.symbol("x");
let e = x.pow(&ctx.rational(5, 2));
let s = e.series(&x, &ctx.int(0), 6);
assert!(
!s.is_zero_structural(),
"series(x^(5/2), x, 0, 6) must not be 0"
);
assert!(
s.has_unevaluated(),
"series(x^(5/2), x, 0, 6) should be unevaluated: {s}"
);
}
#[test]
fn series_sqrt_x_times_x_not_zero_at_order_3() {
let ctx = Context::new();
let x = ctx.symbol("x");
let e = x.sqrt() * &x;
let s = e.series(&x, &ctx.int(0), 3);
assert!(
!s.is_zero_structural(),
"series(sqrt(x)*x, x, 0, 3) must not be 0"
);
assert!(
s.has_unevaluated(),
"series(sqrt(x)*x, x, 0, 3) should be unevaluated: {s}"
);
}
#[test]
fn series_sqrt_of_even_valuation_still_expands() {
let ctx = Context::new();
let x = ctx.symbol("x");
let e = (x.powi(4) + x.powi(6)).sqrt();
let expected = x.powi(2) + x.powi(4) * ctx.rational(1, 2);
assert_series_is(&e, &x, &expected, 0.01005);
}