use crate::Float;
use alloc::vec;
use core::cmp::Ordering;
use core::mem::swap;
use malachite_base::num::arithmetic::traits::CeilingLogBase2;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{One, Zero};
use malachite_base::num::conversion::traits::WrappingFrom;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::integer::Integer;
use malachite_nz::natural::arithmetic::float_extras::float_can_round;
use malachite_nz::platform::Limb;
fn sum(t: &mut [Integer], p: &mut [Integer], q: &mut [Integer], n1: u64, n2: u64, need_p: bool) {
if n2 == n1 + 1 {
p[0] = if n1 == 0 {
const { Integer::const_from_unsigned(3) }
} else {
-Integer::from(n1)
};
q[0] = ((Integer::from(n1) << 1u32) + Integer::ONE) << 2u32;
t[0].clone_from(&p[0]);
} else {
let m = (n1 >> 1) + (n2 >> 1) + (n1 & 1 & n2);
sum(t, p, q, n1, m, true);
let (t_head, t_tail) = t.split_first_mut().unwrap();
let (p_head, p_tail) = p.split_first_mut().unwrap();
let (q_head, q_tail) = q.split_first_mut().unwrap();
sum(t_tail, p_tail, q_tail, m, n2, need_p);
*t_head *= &q_tail[0];
t_tail[0] *= &*p_head;
*t_head += &t_tail[0];
if need_p {
*p_head *= &p_tail[0];
}
*q_head *= &q_tail[0];
let mut tz = t_head.trailing_zeros().unwrap();
if tz != 0 {
let mut qz = q_head.trailing_zeros().unwrap();
if qz < tz {
tz = qz;
}
if need_p {
qz = p_head.trailing_zeros().unwrap();
if qz < tz {
tz = qz;
}
}
if tz != 0 {
*t_head >>= tz;
*q_head >>= tz;
if need_p {
*p_head >>= tz;
}
}
}
}
}
impl Float {
pub fn ln_2_prec_round(prec: u64, rm: RoundingMode) -> (Self, Ordering) {
let mut working_prec = prec + prec.ceiling_log_base_2() + 3;
let mut increment = Limb::WIDTH;
loop {
let big_n = working_prec / 3 + 1;
assert!(working_prec >= 3 && big_n >= 2);
let lg_big_n = usize::wrapping_from(big_n.ceiling_log_base_2()) + 1;
let mut scratch = vec![Integer::ZERO; 3 * lg_big_n];
split_into_chunks_mut!(scratch, lg_big_n, [t, p], q);
sum(t, p, q, 0, big_n, false);
let mut t0 = Integer::ZERO;
let mut q0 = Integer::ZERO;
swap(&mut t0, &mut t[0]);
swap(&mut q0, &mut q[0]);
let ln_2 = Self::from_integer_prec(t0, working_prec).0
/ Self::from_integer_prec(q0, working_prec).0;
if float_can_round(ln_2.significand_ref().unwrap(), working_prec - 2, prec, rm) {
return Self::from_float_prec_round(ln_2, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
#[inline]
pub fn ln_2_prec(prec: u64) -> (Self, Ordering) {
Self::ln_2_prec_round(prec, Nearest)
}
}