use crate::natural::arithmetic::mul::limb::limbs_mul_limb_to_out;
use crate::natural::arithmetic::shl::limbs_shl_to_out;
use crate::natural::arithmetic::square::{limbs_square_to_out, limbs_square_to_out_scratch_len};
use crate::natural::limb_to_bit_count;
use crate::platform::{DoubleLimb, Limb};
use alloc::vec::Vec;
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{NegAssign, Parity};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::slices::{slice_leading_zeros, slice_test_zero};
const WIDTH_I64: i64 = Limb::WIDTH as i64;
#[doc(hidden)]
pub fn limbs_float_exp(xs: &mut [Limb], base: u64, e: i64) -> (i64, i32) {
let len = xs.len();
assert_ne!(len, 0);
assert!(e > 0);
assert!(const { 2..=62 }.contains(&base));
let bit_len = i64::exact_from(limb_to_bit_count(len));
let mut limb_base = Limb::exact_from(base);
let mut h = i64::from(limb_base.leading_zeros());
limb_base <<= h;
h.neg_assign();
let two_len = len << 1;
let mut ys = vec![0; two_len];
let mut square_scratch: Vec<Limb> = Vec::new();
let (xs_last, xs_init) = xs.split_last_mut().unwrap();
*xs_last = limb_base;
xs_init.fill(0);
let mut f = h - (bit_len - WIDTH_I64);
let t = i32::exact_from(e.significant_bits());
let mut error = t;
let mut err_s_a2: i32 = 0;
let mut err_s_ab: i32 = 0;
for i in (0..=t - 2).rev() {
let xs_zeros = slice_leading_zeros(xs);
let two_n1 = xs_zeros << 1;
square_scratch.resize(limbs_square_to_out_scratch_len(len - xs_zeros), 0);
limbs_square_to_out(&mut ys[two_n1..], &xs[xs_zeros..], &mut square_scratch);
if !const { i64::MIN >> 1..=i64::MAX >> 1 }.contains(&f) {
return (f, -2);
}
f <<= 1;
if let Some(g) = f.checked_add(bit_len) {
f = g;
} else {
fail_on_untested_path("limbs_float_exp, f overflow in checked_add");
return (f, -2);
}
let (ys_lo, ys_hi) = ys.split_at(len);
if ys_hi.last().unwrap().get_highest_bit() {
xs.copy_from_slice(ys_hi);
} else {
limbs_shl_to_out(xs, ys_hi, 1);
xs[0] |= Limb::from(ys_lo.last().unwrap().get_highest_bit());
f -= 1;
if error != t {
err_s_a2 += 1;
}
}
if error == t && two_n1 <= len && !slice_test_zero(&ys_lo[two_n1..]) {
error = i;
}
if (e >> i).odd() {
let (ys_last, ys_init) = ys.split_last_mut().unwrap();
let carry =
limbs_mul_limb_to_out::<DoubleLimb, Limb>(&mut ys_init[len - 1..], xs, limb_base);
*ys_last = carry;
f += h + WIDTH_I64;
let (ys_lo, ys_hi) = ys.split_at(len);
if ys_hi.last().unwrap().get_highest_bit() {
xs.copy_from_slice(ys_hi);
if error != t {
err_s_ab += 1;
}
} else {
limbs_shl_to_out(xs, ys_hi, 1);
xs[0] |= Limb::from(ys_lo.last().unwrap().get_highest_bit());
f -= 1;
}
if error == t && *ys_lo.last().unwrap() != 0 {
error = i;
}
}
}
(
f,
if error == t {
-1 } else {
error + err_s_ab + (err_s_a2 >> 1) + 3
},
)
}