use rust_decimal::Decimal;
use rust_decimal::MathematicalOps;
use std::str::FromStr;
fn half_pi() -> Decimal {
Decimal::from_str("1.5707963267948966192313216916").expect("BUG: π/2 constant failed to parse")
}
pub fn decimal_atan(x: Decimal) -> Option<Decimal> {
if x.is_zero() {
return Some(Decimal::ZERO);
}
let abs_x = x.abs();
if abs_x > Decimal::ONE {
let recip = Decimal::ONE.checked_div(x)?;
let inner = decimal_atan(recip)?;
let sign = if x > Decimal::ZERO {
Decimal::ONE
} else {
Decimal::NEGATIVE_ONE
};
let hp = half_pi();
return (sign * hp).checked_sub(inner);
}
let x2 = x.checked_mul(x)?;
let mut term = x;
let mut result = x;
let mut n: i64 = 1;
loop {
n += 2;
term = term.checked_mul(x2)?;
term = term.checked_mul(Decimal::NEGATIVE_ONE)?;
let contribution = term.checked_div(Decimal::from(n))?;
let prev = result;
result = result.checked_add(contribution)?;
if result == prev {
break;
}
}
Some(result)
}
pub fn decimal_asin(x: Decimal) -> Option<Decimal> {
let hp = half_pi();
if x == Decimal::ONE {
return Some(hp);
}
if x == Decimal::NEGATIVE_ONE {
return Some(-hp);
}
if x.abs() > Decimal::ONE {
return None;
}
let one_minus_x2 = Decimal::ONE.checked_sub(x.checked_mul(x)?)?;
let denom = one_minus_x2.sqrt()?;
if denom.is_zero() {
return None;
}
decimal_atan(x.checked_div(denom)?)
}
pub fn decimal_acos(x: Decimal) -> Option<Decimal> {
let asin_x = decimal_asin(x)?;
half_pi().checked_sub(asin_x)
}