use crate::natural::arithmetic::div_mod::{limbs_div_limb_to_out_mod, limbs_div_mod_to_out};
use crate::natural::arithmetic::float::exp::limbs_float_exp;
use crate::natural::arithmetic::float::round::{round_helper_2, round_helper_raw};
use crate::natural::arithmetic::mul::limbs_mul;
use crate::natural::arithmetic::shl::limbs_slice_shl_in_place;
use crate::natural::arithmetic::shr::limbs_slice_shr_in_place;
use crate::natural::conversion::digits::general_digits::limbs_from_digits_small_base;
use crate::natural::{bit_to_limb_count_ceiling, limb_to_bit_count};
use crate::platform::Limb;
use alloc::vec::Vec;
use core::cmp::max;
use malachite_base::num::arithmetic::traits::{CeilingLogBase2, CheckedLogBase2, DivMod, Parity};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_base::slices::slice_test_zero;
const RED_INV_LOG_2: [(u16, u16); 61] = [
(1, 1),
(53, 84),
(1, 2),
(4004, 9297),
(53, 137),
(2393, 6718),
(1, 3),
(665, 2108),
(4004, 13301),
(949, 3283),
(53, 190),
(5231, 19357),
(2393, 9111),
(247, 965),
(1, 4),
(4036, 16497),
(665, 2773),
(5187, 22034),
(4004, 17305),
(51, 224),
(949, 4232),
(3077, 13919),
(53, 243),
(73, 339),
(5231, 24588),
(665, 3162),
(2393, 11504),
(4943, 24013),
(247, 1212),
(3515, 17414),
(1, 5),
(4415, 22271),
(4036, 20533),
(263, 1349),
(665, 3438),
(1079, 5621),
(5187, 27221),
(2288, 12093),
(4004, 21309),
(179, 959),
(51, 275),
(495, 2686),
(949, 5181),
(3621, 19886),
(3077, 16996),
(229, 1272),
(53, 296),
(109, 612),
(73, 412),
(1505, 8537),
(5231, 29819),
(283, 1621),
(665, 3827),
(32, 185),
(2393, 13897),
(1879, 10960),
(4943, 28956),
(409, 2406),
(247, 1459),
(231, 1370),
(3515, 20929),
];
fn limbs_set_str_helper(out: &mut [Limb], digits: &[u8], base: u64) -> usize {
if let Some(bits) = base.checked_log_base_2() {
let mut len = 0;
let mut digit_out = 0;
let mut next_bit_index = 0;
for &digit in digits.iter().rev() {
let digit = Limb::from(digit);
digit_out |= digit << next_bit_index;
next_bit_index += bits;
if next_bit_index >= Limb::WIDTH {
out[len] = digit_out;
len += 1;
next_bit_index -= Limb::WIDTH;
digit_out = digit >> (bits - next_bit_index);
}
}
if digit_out != 0 {
out[len] = digit_out;
len += 1;
}
len
} else {
limbs_from_digits_small_base(out, digits, base).unwrap()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SetStrResult {
Finite(Vec<Limb>, i64, i8),
Overflow,
Underflow,
}
fn add_exp(x: i64, y: i64) -> Result<i64, bool> {
x.checked_add(y).ok_or(y > 0)
}
const fn out_of_range(overflow: bool) -> SetStrResult {
if overflow {
SetStrResult::Overflow
} else {
SetStrResult::Underflow
}
}
#[doc(hidden)]
pub fn limbs_set_str(
digits: &[u8],
base: u64,
exp_base: i64,
exp_bin: i64,
prec_x: u64,
rm: RoundingMode,
) -> SetStrResult {
let digits_len = digits.len();
assert_ne!(digits_len, 0);
assert!(const { 2..=62 }.contains(&base));
assert_ne!(prec_x, 0);
let mut prec = prec_x + prec_x.ceiling_log_base_2();
let mut ziv_step = Limb::WIDTH;
let (result, ysize_bits, mut exp) = loop {
let ysize = bit_to_limb_count_ceiling(prec);
let ysize_bits = limb_to_bit_count(ysize);
let (num, den) = RED_INV_LOG_2[usize::exact_from(base) - 2];
let (num, den) = (u64::from(num), u64::from(den));
let (a, b) = ysize_bits.div_mod(den);
let mut pstr_size = usize::exact_from(a * num + (b * num).div_ceil(den) + 1);
if pstr_size > digits_len {
pstr_size = digits_len;
}
let y_len =
bit_to_limb_count_ceiling(u64::exact_from(pstr_size) * base.ceiling_log_base_2()) + 1;
let mut y0 = vec![0; ysize + max(ysize, y_len)];
let real_ysize = limbs_set_str_helper(&mut y0[ysize..], &digits[..pstr_size], base);
let mut exact = pstr_size == digits_len;
let y = &mut y0[ysize..];
assert_ne!(y[real_ysize - 1], 0);
let count = u64::from(y[real_ysize - 1].leading_zeros());
let mut exp = if let Some(diff_ysize) = ysize.checked_sub(real_ysize) {
if count != 0 {
limbs_slice_shl_in_place(&mut y[..real_ysize], count);
}
if diff_ysize != 0 {
y.copy_within(0..real_ysize, diff_ysize);
y[..diff_ysize].fill(0);
}
-(i64::exact_from(limb_to_bit_count(diff_ysize)) + i64::exact_from(count))
} else {
let dropped = real_ysize - ysize - 1;
if dropped != 0 {
exact = exact && slice_test_zero(&y[..dropped]);
y.copy_within(dropped..real_ysize, 0);
}
let kept = ysize + 1;
if count != 0 {
if limbs_slice_shr_in_place(&mut y[..kept], Limb::WIDTH - count) != 0 {
exact = false;
}
} else {
exact = exact && y[0] == 0;
y.copy_within(1..kept, 0);
}
i64::exact_from(limb_to_bit_count(dropped + 1)) - i64::exact_from(count)
};
let pstr_size_i = i64::exact_from(pstr_size);
let ysize_bits_i = i64::exact_from(ysize_bits);
let mut err;
let mut product;
let result_offset;
if let Some(pow2) = base.checked_log_base_2() {
let pow2 = i64::exact_from(pow2);
let mut tmp = match add_exp(exp_base, -pstr_size_i) {
Ok(tmp) => tmp,
Err(over) => return out_of_range(over),
};
tmp = match tmp.checked_mul(pow2) {
Some(tmp) => tmp,
None => return out_of_range(tmp > 0),
};
tmp = match add_exp(tmp, exp_bin) {
Ok(tmp) => tmp,
Err(over) => return out_of_range(over),
};
exp = match add_exp(exp, tmp) {
Ok(exp) => exp,
Err(over) => return out_of_range(over),
};
product = y0;
result_offset = ysize;
err = 0;
} else if exp_base > pstr_size_i {
let (y0_lo, y_hi) = y0.split_at_mut(ysize);
let (mut exp_z, err_z) = limbs_float_exp(y0_lo, base, exp_base - pstr_size_i);
if err_z == -2 {
return SetStrResult::Overflow;
}
exact = exact && err_z == -1;
product = limbs_mul(&y_hi[..ysize], y0_lo);
err = if err_z == -1 { 0 } else { i64::from(err_z) } + 1;
exp_z = match add_exp(exp_z, ysize_bits_i) {
Ok(exp_z) => exp_z,
Err(over) => return out_of_range(over),
};
exp = match add_exp(exp, exp_z) {
Ok(exp) => exp,
Err(over) => return out_of_range(over),
};
if !product[(ysize << 1) - 1].get_highest_bit() {
limbs_slice_shl_in_place(&mut product[ysize - 1..], 1);
exp -= 1;
}
exact = exact && slice_test_zero(&product[..ysize]);
result_offset = ysize;
} else if exp_base < pstr_size_i {
y0[..ysize].fill(0);
let neg_exp_base = if exp_base == i64::MIN {
i64::MAX
} else {
-exp_base
};
let mut exp_z = match add_exp(pstr_size_i, neg_exp_base) {
Ok(exp_z) => exp_z,
Err(over) => return out_of_range(!over),
};
let mut z = vec![0; ysize];
let err_z;
(exp_z, err_z) = limbs_float_exp(&mut z, base, exp_z);
if err_z == -2 {
return SetStrResult::Underflow;
} else if err_z == -1 {
err = 0;
} else {
err = i64::from(err_z);
exact = false;
}
exp_z = match add_exp(exp_z, ysize_bits_i) {
Ok(exp_z) => exp_z,
Err(over) => return out_of_range(!over),
};
exp = match add_exp(exp, -exp_z) {
Ok(exp) => exp,
Err(over) => return out_of_range(over),
};
assert!(y0[(ysize << 1) - 1].get_highest_bit());
assert!(z[ysize - 1].get_highest_bit());
let mut quotient = vec![0; (ysize << 1) + 1];
let (qs, remainder) = quotient.split_at_mut(ysize + 1);
if ysize == 1 {
remainder[0] = limbs_div_limb_to_out_mod(qs, &y0[..2], z[0]);
} else {
limbs_div_mod_to_out(qs, remainder, &y0[..ysize << 1], &z);
}
assert!(qs[ysize] <= 1);
err += 1;
exact = exact && slice_test_zero(remainder);
quotient.truncate(ysize + 1);
if quotient[ysize] == 1 {
exact = exact && quotient[0].even();
limbs_slice_shr_in_place(&mut quotient, 1);
exp += 1;
}
product = quotient;
result_offset = 0;
} else {
product = y0;
result_offset = ysize;
err = 0;
}
let result = &product[result_offset..result_offset + ysize];
if exact
|| round_helper_2(
result,
i32::exact_from(ysize_bits_i - err - 1),
prec_x + u64::from(rm == Nearest),
)
{
break (result.to_vec(), ysize_bits, exp);
}
prec += ziv_step;
ziv_step = prec >> 1;
};
let mut out = vec![0; bit_to_limb_count_ceiling(prec_x)];
let (dir, increment) = round_helper_raw(&mut out, prec_x, &result, ysize_bits, rm);
if increment {
exp += 1;
}
match add_exp(exp, i64::exact_from(ysize_bits)) {
Ok(exp) => SetStrResult::Finite(out, exp, dir),
Err(_) => SetStrResult::Overflow,
}
}