use super::decimal_compute::{
ComputeStorage, DECIMAL_COMPUTE_DP,
decimal_compute_zero, decimal_compute_one, decimal_compute_halve,
decimal_compute_add, decimal_compute_sub, decimal_compute_mul,
decimal_compute_div,
decimal_compute_is_zero, decimal_compute_is_negative, decimal_compute_abs,
decimal_compute_cmp, decimal_compute_neg,
};
use super::sqrt::decimal_sqrt;
use super::sin_cos::pi_at_decimal_compute;
use crate::fixed_point::domains::symbolic::rational::rational_number::OverflowDetected;
const fn max_taylor_terms() -> u32 {
(DECIMAL_COMPUTE_DP as u32 * 2) + 20
}
fn atan_taylor(x: ComputeStorage) -> ComputeStorage {
if decimal_compute_is_zero(&x) {
return decimal_compute_zero();
}
let x_sq = decimal_compute_mul(x, x);
let mut term = x; let mut sum = x;
let mut sign_positive = true;
let max_terms = max_taylor_terms();
for k in 1..=max_terms {
term = decimal_compute_mul(term, x_sq);
if decimal_compute_is_zero(&term) {
break;
}
sign_positive = !sign_positive;
let divisor = (2 * k as u64) + 1;
let contribution = super::decimal_compute::decimal_compute_div_int(term, divisor);
if decimal_compute_is_zero(&contribution) {
break;
}
if sign_positive {
sum = decimal_compute_add(sum, contribution);
} else {
sum = decimal_compute_sub(sum, contribution);
}
}
sum
}
pub fn decimal_atan(x: ComputeStorage) -> Result<ComputeStorage, OverflowDetected> {
if decimal_compute_is_zero(&x) {
return Ok(decimal_compute_zero());
}
let negative = decimal_compute_is_negative(&x);
let abs_x = decimal_compute_abs(x);
let one = decimal_compute_one();
let cmp_one = decimal_compute_cmp(&abs_x, &one);
let result = if cmp_one == std::cmp::Ordering::Greater {
let reciprocal = decimal_compute_div(one, abs_x)?;
let atan_recip = atan_reduced(reciprocal)?;
let pi_half = decimal_compute_halve(pi_at_decimal_compute()?);
decimal_compute_sub(pi_half, atan_recip)
} else {
atan_reduced(abs_x)?
};
if negative {
Ok(decimal_compute_neg(result))
} else {
Ok(result)
}
}
fn atan_reduced(x: ComputeStorage) -> Result<ComputeStorage, OverflowDetected> {
let threshold = tan_pi_over_16_approx();
let mut y = x;
let mut halvings: u32 = 0;
while decimal_compute_cmp(&y, &threshold) == std::cmp::Ordering::Greater && halvings < 8 {
let y_sq = decimal_compute_mul(y, y);
let one_plus_y_sq = decimal_compute_add(decimal_compute_one(), y_sq);
let sqrt_val = decimal_sqrt(one_plus_y_sq)?;
let denom = decimal_compute_add(decimal_compute_one(), sqrt_val);
y = decimal_compute_div(y, denom)?;
halvings += 1;
}
let mut result = atan_taylor(y);
for _ in 0..halvings {
result = decimal_compute_add(result, result);
}
Ok(result)
}
fn tan_pi_over_16_approx() -> ComputeStorage {
use super::decimal_compute::pow10_compute_ct;
if DECIMAL_COMPUTE_DP >= 1 {
let two = {
#[cfg(table_format = "q16_16")]
{ 2i64 }
#[cfg(table_format = "q32_32")]
{ 2i128 }
#[cfg(table_format = "q64_64")]
{ crate::fixed_point::i256::I256::from_i128(2) }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::i512::I512::from_i128(2) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::I1024::from_i128(2) }
};
pow10_compute_ct(DECIMAL_COMPUTE_DP - 1) * two
} else {
decimal_compute_one()
}
}
pub fn decimal_atan2(y: ComputeStorage, x: ComputeStorage) -> Result<ComputeStorage, OverflowDetected> {
let y_zero = decimal_compute_is_zero(&y);
let x_zero = decimal_compute_is_zero(&x);
if x_zero && y_zero {
return Err(OverflowDetected::DomainError);
}
let pi = pi_at_decimal_compute()?;
let pi_half = decimal_compute_halve(pi);
if x_zero {
return if decimal_compute_is_negative(&y) {
Ok(decimal_compute_neg(pi_half))
} else {
Ok(pi_half)
};
}
let ratio = decimal_compute_div(y, x)?;
let atan_val = decimal_atan(ratio)?;
if !decimal_compute_is_negative(&x) {
Ok(atan_val)
} else if !decimal_compute_is_negative(&y) {
Ok(decimal_compute_add(atan_val, pi))
} else {
Ok(decimal_compute_sub(atan_val, pi))
}
}
#[cfg(all(test, table_format = "q64_64"))]
mod tests {
use super::*;
use super::super::decimal_compute::pow10_compute_ct;
use crate::fixed_point::i256::I256;
fn parse_decimal_str(s: &str) -> I256 {
let mut result = I256::from_i128(0);
let ten = I256::from_i128(10);
for ch in s.chars() {
let digit = ch.to_digit(10).expect("non-digit");
result = result * ten + I256::from_i128(digit as i128);
}
result
}
#[test]
fn atan_zero() {
let result = decimal_atan(decimal_compute_zero()).unwrap();
assert_eq!(result, decimal_compute_zero());
}
#[test]
fn atan_one_is_pi_over_4() {
let one = decimal_compute_one();
let result = decimal_atan(one).unwrap();
let expected = parse_decimal_str("78539816339744830961566084581987572105");
let diff = if result > expected { result - expected } else { expected - result };
let tolerance = I256::from_i128(1_000_000);
assert!(
diff < tolerance,
"atan(1) precision: got={:?} expected={:?} diff={:?}",
result, expected, diff
);
}
#[test]
fn atan_half() {
let half = pow10_compute_ct(37) * I256::from_i128(5);
let result = decimal_atan(half).unwrap();
let expected = parse_decimal_str("46364760900080611621425623146121440203");
let diff = if result > expected { result - expected } else { expected - result };
let tolerance = I256::from_i128(1_000_000);
assert!(
diff < tolerance,
"atan(0.5) precision: got={:?} expected={:?} diff={:?}",
result, expected, diff
);
}
}