#[allow(unused_imports)]
use super::word::DecimalWord;
#[allow(unused_imports)]
use crate::fixed_point::width::WideInt;
#[allow(unused_imports)]
use super::decimal_compute::{decimal_compute_one, decimal_compute_halve, decimal_compute_add, decimal_compute_sub, decimal_compute_mul, decimal_compute_div, decimal_compute_cmp, decimal_compute_neg};
use crate::fixed_point::domains::symbolic::rational::rational_number::OverflowDetected;
fn ln2_at_compute<C: DecimalWord>() -> Result<C, OverflowDetected> {
struct Ln2<C>(C);
Ok(super::cache::typed_cached(|| Ok(Ln2(compute_ln2::<C>()?)))?.0)
}
fn compute_ln2<C: DecimalWord>() -> Result<C, OverflowDetected> {
let one = decimal_compute_one::<C>();
let three = C::decimal_compute_from_int(3);
let one_third = decimal_compute_div(one, three)?;
let s = one_third;
let s_sq = decimal_compute_mul(s, s);
let mut term = s;
let mut sum = s;
let max_terms = (C::DP as u32) + 20;
for k in 1..=max_terms {
term = decimal_compute_mul(term, s_sq);
if C::decimal_compute_is_zero(&term) {
break;
}
let divisor = (2 * k as u64) + 1;
let contribution = C::decimal_compute_div_int(term, divisor);
if C::decimal_compute_is_zero(&contribution) {
break;
}
sum = decimal_compute_add(sum, contribution);
}
let ln2 = decimal_compute_add(sum, sum);
Ok(ln2)
}
pub fn decimal_ln<C: DecimalWord>(x: C) -> Result<C, OverflowDetected> {
if C::decimal_compute_is_negative(&x) || C::decimal_compute_is_zero(&x) {
return Err(OverflowDetected::DomainError);
}
let one = decimal_compute_one::<C>();
let two = C::decimal_compute_from_int(2);
let mut m = x;
let mut k: i64 = 0;
while decimal_compute_cmp(&m, &two) != std::cmp::Ordering::Less {
m = decimal_compute_halve(m);
k += 1;
}
while decimal_compute_cmp(&m, &one) == std::cmp::Ordering::Less {
m = decimal_compute_add(m, m);
k -= 1;
}
let m_minus_one = decimal_compute_sub(m, one);
let m_plus_one = decimal_compute_add(m, one);
let s = decimal_compute_div(m_minus_one, m_plus_one)?;
let s_sq = decimal_compute_mul(s, s);
let mut term = s;
let mut atanh_sum = s;
let max_terms = (C::DP as u32) + 20;
for j in 1..=max_terms {
term = decimal_compute_mul(term, s_sq);
if C::decimal_compute_is_zero(&term) {
break;
}
let divisor = (2 * j as u64) + 1;
let contribution = C::decimal_compute_div_int(term, divisor);
if C::decimal_compute_is_zero(&contribution) {
break;
}
atanh_sum = decimal_compute_add(atanh_sum, contribution);
}
let ln_m = decimal_compute_add(atanh_sum, atanh_sum);
if k == 0 {
return Ok(ln_m);
}
let ln2 = ln2_at_compute::<C>()?;
let k_ln2 = if k > 0 {
C::mul_compute_by_int(ln2, k as u64)
} else {
decimal_compute_neg(C::mul_compute_by_int(ln2, (-k) as u64))
};
Ok(decimal_compute_add(k_ln2, ln_m))
}
#[cfg(test)]
mod tests {
use super::*;
type ComputeStorage = crate::fixed_point::I256;
#[allow(unused_imports)]
use super::super::decimal_compute::*;
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 ln_one_is_zero() {
let result = decimal_ln(decimal_compute_one::<ComputeStorage>()).unwrap();
assert_eq!(result, decimal_compute_zero::<ComputeStorage>());
}
#[test]
fn ln_negative_errors() {
let neg_one = decimal_compute_neg(decimal_compute_one::<ComputeStorage>());
assert!(decimal_ln(neg_one).is_err());
}
#[test]
fn ln_zero_errors() {
assert!(decimal_ln(decimal_compute_zero::<ComputeStorage>()).is_err());
}
#[test]
fn ln_two_mpmath() {
let two = decimal_compute_from_int::<ComputeStorage>(2);
let result = decimal_ln(two).unwrap();
let expected = parse_decimal_str("69314718055994530941723212145817656808");
let diff = if result > expected { result - expected } else { expected - result };
let tolerance = I256::from_i128(1_000_000);
assert!(
diff < tolerance,
"ln(2) precision: got={:?} expected={:?} diff={:?}",
result, expected, diff
);
}
#[test]
fn ln_ten_mpmath() {
let ten = decimal_compute_from_int::<ComputeStorage>(10);
let result = decimal_ln(ten).unwrap();
let expected = parse_decimal_str("230258509299404568401799145468436420760");
let diff = if result > expected { result - expected } else { expected - result };
let tolerance = I256::from_i128(10_000_000);
assert!(
diff < tolerance,
"ln(10) precision: got={:?} expected={:?} diff={:?}",
result, expected, diff
);
}
#[test]
fn ln_half_mpmath() {
let half = pow10_compute_ct::<ComputeStorage>(37) * I256::from_i128(5);
let result = decimal_ln(half).unwrap();
let expected_abs = parse_decimal_str("69314718055994530941723212145817656808");
let result_abs = if decimal_compute_is_negative(&result) {
decimal_compute_neg(result)
} else {
result
};
let diff = if result_abs > expected_abs { result_abs - expected_abs } else { expected_abs - result_abs };
let tolerance = I256::from_i128(1_000_000);
assert!(
diff < tolerance,
"ln(0.5) precision: got={:?} expected_abs={:?}",
result, expected_abs
);
assert!(decimal_compute_is_negative(&result), "ln(0.5) should be negative");
}
}