use symplex::prelude::*;
fn parse(ctx: &Context, s: &str) -> Ex {
ctx.parse(s).unwrap()
}
fn refused_with(ctx: &Context, s: &str, want: &str) {
for digits in [16, 30] {
match parse(ctx, s).eval_decimal(digits) {
Err(SymplexError::Unevaluable { reason }) if reason.contains(want) => {}
other => panic!("{s} at {digits} digits: want \"{want}\", got {other:?}"),
}
}
}
#[test]
fn second_order_cancellations_below_the_range_are_not_zero() {
let ctx = Context::new();
for s in [
"log(1+exp(-10^10)) - exp(-10^10)",
"sin(exp(-10^10)) - exp(-10^10)",
"exp(exp(-10^10)) - 1 - exp(-10^10)",
"cos(exp(-10^10)) - 1 + exp(-2*10^10)/2",
"cos(exp(-10^10)) - 1 + exp(-10^10)^2/2",
"(1+exp(-10^10))*exp(-10^10) - exp(-10^10)",
"sqrt(1+exp(-10^10)) - 1 - exp(-10^10)/2",
"log(1+exp(-10^10)) - exp(-10^10) - (cos(exp(-10^10)) - 1)",
] {
refused_with(&ctx, s, "is not 0 but underflows");
assert_eq!(parse(&ctx, s).eval_f64().unwrap(), 0.0, "{s}");
}
}
#[test]
fn logarithms_of_the_residuals_are_in_range() {
let ctx = Context::new();
let dec = |s: &str| parse(&ctx, s).eval_decimal(30).unwrap();
assert_eq!(
dec("log(-(log(1+exp(-10^10)) - exp(-10^10)))"),
"-20000000000.6931471805599453094"
);
assert_eq!(
dec("log(exp(exp(-10^10)) - 1 - exp(-10^10))"),
"-20000000000.6931471805599453094"
);
assert_eq!(
dec("log(-(sin(exp(-10^10)) - exp(-10^10)))"),
"-30000000001.7917594692280550008"
);
assert_eq!(
dec("log(cos(exp(-10^10)) - 1 + exp(-10^10)^2/2)"),
"-40000000003.1780538303479456196"
);
assert_eq!(
dec("log(tan(exp(-10^10)) - exp(-10^10))"),
"-30000000001.0986122886681096914"
);
assert_eq!(
dec("log(log(1+exp(-10^10)) - exp(-10^10) - (cos(exp(-10^10)) - 1))"),
"-30000000001.0986122886681096914"
);
assert_eq!(
dec("log(sqrt(1+exp(-10^10)) - 1 - exp(-10^10)/2)"),
"-20000000002.0794415416798359283 + 3.14159265358979323846264338328*i"
);
}
#[test]
fn a_cancellation_beyond_the_kept_orders_is_refused() {
let ctx = Context::new();
refused_with(
&ctx,
"sin(exp(-10^10)) - exp(-10^10) + exp(-3*10^10)/6 - exp(-5*10^10)/120",
"not known to be 0",
);
}
#[test]
fn true_zeros_below_the_range_stay_zero() {
let ctx = Context::new();
for s in [
"exp(-10^10)*(sin(1)^2+cos(1)^2) - exp(-10^10)",
"exp(-3*10^9)^(1/3) - exp(-10^9)",
"exp(-10^10)^2 - exp(-2*10^10)",
"(1+exp(-10^10))^2 - 1 - 2*exp(-10^10) - exp(-2*10^10)",
] {
assert_eq!(parse(&ctx, s).eval_decimal(16).unwrap(), "0", "{s}");
}
}