use crate::{Ratio, BigInt, ln2_iter};
pub fn ln_iter(x: &Ratio, iter: usize) -> Ratio {
if x.is_neg() {
panic!("logarithm of a negative number is undefined");
}
else if x.is_zero() {
panic!("logarithm of 0 is undefined");
}
let log2_approx = x.numer.log2_accurate().sub_bi(&x.denom.log2_accurate()).shift_right(1).to_i64().unwrap();
let mut x_iter = x.clone();
let mut log2_approx_counter = log2_approx.abs();
if log2_approx > 0 {
while log2_approx_counter > 0 && x_iter.numer.rem_pow2(2).is_zero() {
log2_approx_counter -= 1;
x_iter.numer.div_i32_mut(2);
}
if log2_approx_counter % 32 == 31 {
log2_approx_counter -= 1;
x_iter.denom.mul_i32_mut(2);
}
x_iter.denom.mul_i32_mut((1 << (log2_approx_counter % 32)) as i32);
x_iter.denom.shift_left_mut((log2_approx_counter / 32) as usize);
}
else {
while log2_approx_counter > 0 && x_iter.denom.rem_pow2(2).is_zero() {
log2_approx_counter -= 1;
x_iter.denom.div_i32_mut(2);
}
if log2_approx_counter % 32 == 31 {
log2_approx_counter -= 1;
x_iter.numer.mul_i32_mut(2);
}
x_iter.numer.mul_i32_mut((1 << (log2_approx_counter % 32)) as i32);
x_iter.numer.shift_left_mut((log2_approx_counter / 32) as usize);
}
x_iter.sub_i32_mut(1);
let a = x_iter.clone();
let mut result = a.clone();
for k in 0..iter {
x_iter.mul_rat_mut(&a);
result.sub_rat_mut(&x_iter.div_i32((2 * k + 2) as i32));
x_iter.mul_rat_mut(&a);
result.add_rat_mut(&x_iter.div_i32((2 * k + 3) as i32));
}
result.add_rat(&ln2_iter(iter).mul_bi(&BigInt::from_i64(log2_approx)))
}
pub fn log_iter(base: &Ratio, x: &Ratio, iter: usize) -> Ratio {
ln_iter(x, iter).div_rat(&ln_iter(base, iter))
}
#[cfg(test)]
mod tests {
use crate::{Ratio, BigInt, ln_iter, exp_iter, log_iter};
use crate::utils::are_close;
#[test]
fn ln_test() {
assert_eq!("3.141592", exp_iter(&ln_iter(&Ratio::from_string("3.14159265").unwrap(), 11), 11).to_approx_string(8));
assert_eq!("9.999999", exp_iter(&ln_iter(&Ratio::from_string("10").unwrap(), 6), 6).to_approx_string(8));
assert_eq!("1", ln_iter(&Ratio::from_string("2.718281828459045").unwrap(), 6).to_approx_string(8));
assert_eq!("0.6931471", ln_iter(&Ratio::from_string("2").unwrap(), 6).to_approx_string(9));
assert_eq!("-1.386294", ln_iter(&Ratio::from_string("0.25").unwrap(), 6).to_approx_string(9));
assert_eq!(
std::f64::consts::LN_10,
ln_iter(&10.into(), 11).to_ieee754_f64().unwrap(),
);
}
#[test]
fn log_test() {
assert_eq!(
std::f64::consts::LOG2_10,
log_iter(&2.into(), &10.into(), 12).to_ieee754_f64().unwrap(),
);
assert_eq!(
std::f64::consts::LOG10_2,
log_iter(&10.into(), &2.into(), 11).to_ieee754_f64().unwrap(),
);
let nums = vec![
0.5f64, 1.6, 3.2,
2.0, 1624.5, 4.9,
1.01, 9932.0, 0.1
];
let accr = 3e-8;
for i in 0..nums.len() {
for j in 0..nums.len() {
let a = nums[i];
let b = nums[j];
let ans_f64 = b.log(a);
let a_rat = Ratio::from_ieee754_f64(a).unwrap();
let b_rat = Ratio::from_ieee754_f64(b).unwrap();
let ans_f64_rat = Ratio::from_ieee754_f64(ans_f64).unwrap();
let ans_rat_1 = Ratio::from_denom_and_numer(
BigInt::from_raw(vec![0, 1], false),
b_rat.numer.log2_accurate().sub_bi(&b_rat.denom.log2_accurate())
).div_rat(&Ratio::from_denom_and_numer(
BigInt::from_raw(vec![0, 1], false),
a_rat.numer.log2_accurate().sub_bi(&a_rat.denom.log2_accurate())
));
let ans_rat_2 = log_iter(
&a_rat, &b_rat, 14
);
assert!(are_close(&ans_f64_rat, &ans_rat_1, accr));
assert!(are_close(&ans_f64_rat, &ans_rat_2, accr));
assert!(are_close(&ans_rat_1, &ans_rat_2, accr));
}
}
}
}