use crate::natural::InnerNatural::{Large, Small};
use crate::natural::arithmetic::add::{limbs_add_limb_to_out, limbs_slice_add_limb_in_place};
use crate::natural::arithmetic::div_mod::{limbs_div_limb_to_out_mod, limbs_div_mod_to_out};
use crate::natural::arithmetic::mul::limb::limbs_mul_limb_to_out;
use crate::natural::arithmetic::mul::limbs_mul;
use crate::natural::arithmetic::shl::{limbs_shl_to_out, limbs_slice_shl_in_place};
use crate::natural::arithmetic::shr::{limbs_shr_to_out, limbs_slice_shr_in_place};
use crate::natural::arithmetic::square::{limbs_square_to_out, limbs_square_to_out_scratch_len};
use crate::natural::conversion::digits::general_digits::{
limbs_from_digits_small_base, limbs_to_digits_small_base,
};
use crate::natural::{
LIMB_HIGH_BIT, LIMB_MAX_HALF, Natural, bit_to_limb_count_ceiling, bit_to_limb_count_floor,
limb_to_bit_count,
};
use crate::platform::{DoubleLimb, Limb};
use alloc::vec::Vec;
use core::cmp::Ordering::*;
use core::cmp::{max, min};
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{
CeilingLogBase2, CheckedLogBase2, DivMod, NegAssign, NegModPowerOf2, Parity, PowerOf2,
WrappingSubAssign,
};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::conversion::traits::{ExactFrom, PowerOf2Digits};
use malachite_base::num::logic::traits::{LowMask, SignificantBits};
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_base::slices::{slice_leading_zeros, slice_test_zero};
const WIDTH_I64: i64 = Limb::WIDTH as i64;
pub fn float_can_round(x: &Natural, err0: u64, prec: u64, rm: RoundingMode) -> bool {
match x {
Natural(Small(small)) => limb_float_can_round(*small, err0, prec, rm),
Natural(Large(xs)) => limbs_float_can_round(xs, err0, prec, rm),
}
}
pub(crate) fn limb_float_can_round(x: Limb, err0: u64, mut prec: u64, rm: RoundingMode) -> bool {
if rm == Nearest {
prec += 1;
}
assert!(x.get_highest_bit());
let err = min(err0, u64::power_of_2(Limb::LOG_WIDTH));
if err <= prec {
return false;
}
let mut s = Limb::WIDTH - (prec & Limb::WIDTH_MASK);
let n = bit_to_limb_count_floor(err);
let mask = Limb::low_mask(s);
let mut tmp = x & mask;
s = Limb::WIDTH - (err & Limb::WIDTH_MASK);
if n == 0 {
assert!(s < Limb::WIDTH);
tmp >>= s;
tmp != 0 && tmp != mask >> s
} else if tmp == 0 {
s != Limb::WIDTH && x >> s != 0
} else if tmp == mask {
s != Limb::WIDTH && x >> s != Limb::MAX >> s
} else {
true
}
}
pub fn limbs_float_can_round(xs: &[Limb], err0: u64, mut prec: u64, rm: RoundingMode) -> bool {
if rm == Nearest {
prec += 1;
}
let len = xs.len();
assert!(xs[len - 1].get_highest_bit());
let err = min(err0, limb_to_bit_count(len));
if err <= prec {
return false;
}
let k = bit_to_limb_count_floor(prec);
let mut s = Limb::WIDTH - (prec & Limb::WIDTH_MASK);
let n = bit_to_limb_count_floor(err) - k;
assert!(len > k);
let mut i = len - k - 1;
let mask = Limb::low_mask(s);
let mut tmp = xs[i] & mask;
i.wrapping_sub_assign(1);
if n == 0 {
s = Limb::WIDTH - (err & Limb::WIDTH_MASK);
assert!(s < Limb::WIDTH);
tmp >>= s;
tmp != 0 && tmp != mask >> s
} else if tmp == 0 {
let j = i.wrapping_add(2) - n;
if n > 1 && xs[j..=i].iter().any(|&x| x != 0) {
return true;
}
s = Limb::WIDTH - (err & Limb::WIDTH_MASK);
s != Limb::WIDTH && xs[j - 1] >> s != 0
} else if tmp == mask {
let j = i.wrapping_add(2) - n;
if n > 1 && xs[j..=i].iter().any(|&x| x != Limb::MAX) {
return true;
}
s = Limb::WIDTH - (err & Limb::WIDTH_MASK);
s != Limb::WIDTH && xs[j - 1] >> s != Limb::MAX >> s
} else {
true
}
}
pub fn limbs_float_significand_leading_ones(xs: &[Limb]) -> Option<u64> {
let mut i = xs.len();
let mut count = 0;
while i > 0 && xs[i - 1] == Limb::MAX {
count += Limb::WIDTH;
i -= 1;
}
if i == 0 {
return Some(count);
}
let m = xs[i - 1];
let j = m.leading_ones();
if m << j != 0 {
return None;
}
count += u64::from(j);
if slice_test_zero(&xs[..i - 1]) {
Some(count)
} else {
None
}
}
pub fn float_significand_leading_ones(x: &Natural) -> Option<u64> {
match x {
Natural(Small(small)) => limbs_float_significand_leading_ones(core::slice::from_ref(small)),
Natural(Large(xs)) => limbs_float_significand_leading_ones(xs),
}
}
pub(crate) const MPFR_EVEN_INEX: i8 = 2;
pub(crate) const MPFR_ROUND_FAILED: i8 = 3;
const NEG_MPFR_ROUND_FAILED: i8 = -MPFR_ROUND_FAILED;
pub(crate) fn round_helper_even(
out: &mut [Limb],
out_prec: u64,
xs: &[Limb],
x_prec: u64,
rm: RoundingMode,
) -> (i8, bool) {
round_helper(out, out_prec, xs, x_prec, rm, |out, xs_hi, ulp| {
let ulp_mask = !(ulp - 1);
if xs_hi[0] & ulp == 0 {
out.copy_from_slice(xs_hi);
out[0] &= ulp_mask;
(-MPFR_EVEN_INEX, false)
} else {
let increment = limbs_add_limb_to_out(out, xs_hi, ulp);
if increment {
*out.last_mut().unwrap() = LIMB_HIGH_BIT;
}
out[0] &= ulp_mask;
(MPFR_EVEN_INEX, increment)
}
})
}
#[inline]
pub fn round_helper_raw(
out: &mut [Limb],
out_prec: u64,
xs: &[Limb],
x_prec: u64,
rm: RoundingMode,
) -> (i8, bool) {
round_helper(out, out_prec, xs, x_prec, rm, |out, xs_hi, ulp| {
let ulp_mask = !(ulp - 1);
if xs_hi[0] & ulp == 0 {
out.copy_from_slice(xs_hi);
out[0] &= ulp_mask;
(-1, false)
} else {
let increment = limbs_add_limb_to_out(out, xs_hi, ulp);
if increment {
*out.last_mut().unwrap() = LIMB_HIGH_BIT;
}
out[0] &= ulp_mask;
(1, increment)
}
})
}
#[inline]
pub fn round_helper_raw_aliased(
out_offset: usize,
out_prec: u64,
xs: &mut [Limb],
x_prec: u64,
rm: RoundingMode,
) -> (i8, bool) {
round_helper_aliased(out_offset, out_prec, xs, x_prec, rm, |out, ulp| {
let ulp_mask = !(ulp - 1);
if out[0] & ulp == 0 {
out[0] &= ulp_mask;
(-1, false)
} else {
let increment = limbs_slice_add_limb_in_place(out, ulp);
if increment {
*out.last_mut().unwrap() = LIMB_HIGH_BIT;
}
out[0] &= ulp_mask;
(1, increment)
}
})
}
fn round_helper<F: Fn(&mut [Limb], &[Limb], Limb) -> (i8, bool)>(
out: &mut [Limb],
out_prec: u64,
xs: &[Limb],
x_prec: u64,
rm: RoundingMode,
middle_handler: F,
) -> (i8, bool) {
let xs_len = xs.len();
let out_len = out.len();
if out_prec >= x_prec {
out[out_len - xs_len..].copy_from_slice(xs);
(0, false)
} else {
let shift = out_prec.neg_mod_power_of_2(Limb::LOG_WIDTH);
let i = xs_len.checked_sub(out_len).unwrap();
let mut sticky_bit;
let round_bit;
let ulp = if shift != 0 {
let mask = Limb::power_of_2(shift - 1);
let x = xs[i];
round_bit = x & mask;
sticky_bit = x & (mask - 1);
if rm == Nearest || round_bit == 0 {
let mut to = i;
let mut n = xs_len - out_len;
while n != 0 && sticky_bit == 0 {
to -= 1;
sticky_bit = xs[to];
n -= 1;
}
}
mask << 1
} else {
assert!(out_len < xs_len);
let x = xs[i - 1];
round_bit = x & LIMB_HIGH_BIT;
sticky_bit = x & LIMB_MAX_HALF;
if rm == Nearest || round_bit == 0 {
let mut to = i - 1;
let mut n = xs_len - out_len - 1;
while n != 0 && sticky_bit == 0 {
to -= 1;
sticky_bit = xs[to];
n -= 1;
}
}
1
};
let xs_hi = &xs[i..];
let ulp_mask = !(ulp - 1);
match rm {
Floor | Down | Exact => {
out.copy_from_slice(xs_hi);
out[0] &= ulp_mask;
(if sticky_bit | round_bit != 0 { -1 } else { 0 }, false)
}
Ceiling | Up => {
if sticky_bit | round_bit == 0 {
out.copy_from_slice(xs_hi);
out[0] &= ulp_mask;
(0, false)
} else {
let increment = limbs_add_limb_to_out(out, xs_hi, ulp);
if increment {
out[out_len - 1] = LIMB_HIGH_BIT;
}
out[0] &= ulp_mask;
(1, increment)
}
}
Nearest => {
if round_bit == 0 {
out.copy_from_slice(xs_hi);
out[0] &= ulp_mask;
(if (sticky_bit | round_bit) != 0 { -1 } else { 0 }, false)
} else if sticky_bit == 0 {
middle_handler(out, xs_hi, ulp)
} else {
let increment = limbs_add_limb_to_out(out, xs_hi, ulp);
if increment {
out[out_len - 1] = LIMB_HIGH_BIT;
}
out[0] &= ulp_mask;
(1, increment)
}
}
}
}
}
fn round_helper_aliased<F: Fn(&mut [Limb], Limb) -> (i8, bool)>(
out_offset: usize,
out_prec: u64,
xs: &mut [Limb],
x_prec: u64,
rm: RoundingMode,
middle_handler: F,
) -> (i8, bool) {
let xs_len = xs.len();
let out_len = xs_len - out_offset;
if out_prec >= x_prec {
(0, false)
} else {
let shift = out_prec.neg_mod_power_of_2(Limb::LOG_WIDTH);
let mut sticky_bit;
let round_bit;
let ulp = if shift != 0 {
let mask = Limb::power_of_2(shift - 1);
let x = xs[out_offset];
round_bit = x & mask;
sticky_bit = x & (mask - 1);
if rm == Nearest || round_bit == 0 {
let mut n = out_offset;
while n != 0 && sticky_bit == 0 {
n -= 1;
sticky_bit = xs[n];
}
}
mask << 1
} else {
assert_ne!(out_offset, 0);
let x = xs[out_offset - 1];
round_bit = x & LIMB_HIGH_BIT;
sticky_bit = x & LIMB_MAX_HALF;
if rm == Nearest || round_bit == 0 {
let mut n = out_offset - 1;
while n != 0 && sticky_bit == 0 {
n -= 1;
sticky_bit = xs[n];
}
}
1
};
let out = &mut xs[out_offset..];
let ulp_mask = !(ulp - 1);
match rm {
Floor | Down | Exact => {
out[0] &= ulp_mask;
(if sticky_bit | round_bit != 0 { -1 } else { 0 }, false)
}
Ceiling | Up => {
if sticky_bit | round_bit == 0 {
out[0] &= ulp_mask;
(0, false)
} else {
let increment = limbs_slice_add_limb_in_place(out, ulp);
if increment {
out[out_len - 1] = LIMB_HIGH_BIT;
}
out[0] &= ulp_mask;
(1, increment)
}
}
Nearest => {
if round_bit == 0 {
out[0] &= ulp_mask;
(if (sticky_bit | round_bit) != 0 { -1 } else { 0 }, false)
} else if sticky_bit == 0 {
middle_handler(out, ulp)
} else {
let increment = limbs_slice_add_limb_in_place(out, ulp);
if increment {
out[out_len - 1] = LIMB_HIGH_BIT;
}
out[0] &= ulp_mask;
(1, increment)
}
}
}
}
}
pub(crate) fn round_helper_2(xs: &[Limb], err0: i32, prec: u64) -> bool {
let len = xs.len();
assert!(xs.last().unwrap().get_highest_bit());
let mut err = limb_to_bit_count(len);
if err0 <= 0 {
return false;
}
let err0 = u64::from(err0.unsigned_abs());
if err0 <= prec || prec >= err {
return false;
}
err = min(err, err0);
let k = bit_to_limb_count_floor(prec);
let n = bit_to_limb_count_floor(err) - k;
assert!(len > k);
let xs = &xs[len - k - n - 1..];
let (xs_last, xs_init) = xs[..=n].split_last().unwrap();
let mut tmp = *xs_last;
let mask = Limb::MAX >> (prec & Limb::WIDTH_MASK);
tmp &= mask;
if n == 0 {
let s = Limb::WIDTH - (err & Limb::WIDTH_MASK);
assert!(s < Limb::WIDTH);
tmp >>= s;
tmp != 0 && tmp != mask >> s
} else if tmp == 0 {
let (xs_head, xs_tail) = xs_init.split_first().unwrap();
if !slice_test_zero(xs_tail) {
return true;
}
let s = Limb::WIDTH - (err & Limb::WIDTH_MASK);
s != Limb::WIDTH && *xs_head >> s != 0
} else if tmp == mask {
let (xs_head, xs_tail) = xs_init.split_first().unwrap();
if xs_tail.iter().any(|&x| x != Limb::MAX) {
return true;
}
let s = Limb::WIDTH - (err & Limb::WIDTH_MASK);
s != Limb::WIDTH && *xs_head >> s != Limb::MAX >> s
} else {
true
}
}
#[inline]
pub fn limbs_significand_slice_add_limb_in_place(xs: &mut [Limb], y: Limb) -> bool {
limbs_slice_add_limb_in_place(xs, y)
}
#[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
},
)
}
const NUM_TO_TEXT_36: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
const NUM_TO_TEXT_62: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
pub_test! {limbs_get_str_aux(
out: &mut [u8],
xs: &mut [Limb],
neg_f: u64,
e: i64,
base: i64,
digit_len: usize,
rm: RoundingMode,
) -> (i8, i64) {
let n = xs.len();
let n_width = limb_to_bit_count(n);
assert!(neg_f < n_width);
let b = base.unsigned_abs();
let mut exp = 0;
let exact = e < 0;
if exact
|| round_helper_2(
xs,
i32::exact_from(i64::exact_from(n_width) - e),
n_width - neg_f + u64::from(rm == Nearest),
)
{
let mut i0 = bit_to_limb_count_floor(neg_f);
let j0 = neg_f & Limb::WIDTH_MASK;
let (mut dir, carry) = round_helper_raw_aliased(i0, n_width - neg_f, xs, n_width, rm);
assert_ne!(dir, MPFR_ROUND_FAILED);
if carry {
xs[n - 1] = if j0 != 0 {
LIMB_HIGH_BIT >> (j0 - 1)
} else {
i0 -= 1;
xs[i0] = 0; Limb::from(carry)
};
} else if j0 != 0 {
limbs_slice_shr_in_place(&mut xs[i0..], j0);
}
let mut str1 = vec![0; digit_len + 3];
let size_s1 = limbs_to_digits_small_base(&mut str1, b, &mut xs[i0..], None);
assert!(size_s1 >= digit_len);
exp = i64::exact_from(size_s1 - digit_len);
let size_s1_m1 = size_s1 - 1;
if size_s1 == digit_len + 1 && (dir != 0 || str1[size_s1_m1] != 0) {
let rnd1 = if rm == Nearest {
let twice_last = u64::from(str1[size_s1_m1]) << 1;
match twice_last.cmp(&b) {
Equal => {
if dir == 0 && exact {
if str1[size_s1 - 2].even() {
Floor
} else {
Ceiling
}
} else {
return (NEG_MPFR_ROUND_FAILED, exp);
}
}
Less => Floor,
Greater => Ceiling,
}
} else {
rm
};
if rnd1 == Ceiling || rnd1 == Up {
if str1[size_s1_m1] != 0 {
assert!(size_s1 >= 2);
let mut i = size_s1 - 2;
let target = u8::exact_from(b - 1);
while str1[i] == target {
assert_ne!(i, 0);
str1[i] = 0;
i -= 1;
}
str1[i] += 1;
}
dir = 1;
} else if str1[size_s1_m1] != 0 {
dir = -1;
}
}
let num_to_text = if (2..=36).contains(&base) {
NUM_TO_TEXT_36
} else {
NUM_TO_TEXT_62
};
for i in 0..digit_len {
out[i] = num_to_text[usize::from(str1[i])];
}
(dir, exp)
} else {
(MPFR_ROUND_FAILED, exp)
}
}}
#[doc(hidden)]
pub fn limbs_get_str(
xs: &[Limb],
x_exp: i64,
abs_base: u64,
base: i64,
digit_len: usize,
rm: RoundingMode,
mut g: i64,
mut prec: u64,
mut exp: i64,
) -> (Vec<u8>, i64, i8) {
let xs_len = xs.len();
let digit_len_i = i64::exact_from(digit_len);
let mut ziv_step = Limb::WIDTH;
loop {
let mut exact = true;
let n = bit_to_limb_count_ceiling(prec);
let mut a = vec![0; n];
let mut exp_a: i64;
let mut err: i64;
match digit_len_i.cmp(&g) {
Equal => {
err = if n < xs_len {
let (xs_lo, xs_hi) = xs.split_at(xs_len - n);
exact = slice_test_zero(xs_lo);
a.copy_from_slice(xs_hi);
i64::from(!exact)
} else {
a[n - xs_len..].copy_from_slice(xs);
0
};
exp_a = x_exp - i64::exact_from(limb_to_bit_count(n));
}
Greater => {
let err_e;
(exp_a, err_e) = limbs_float_exp(&mut a, abs_base, exp);
exact = err_e == -1;
let (x1, nx1) = if n < xs_len {
let (xs_lo, xs_hi) = xs.split_at(xs_len - n);
if exact {
exact = slice_test_zero(xs_lo);
}
(xs_hi, n)
} else {
(xs, xs_len)
};
err = if err_e <= 0 { 2 } else { i64::from(err_e) + 1 };
let result = limbs_mul(&a, x1);
let (result_lo, result_hi) = result.split_at(nx1);
let result_hi = &result_hi[..n];
if !slice_test_zero(result_lo) {
exact = false;
}
exp_a += x_exp;
if result_hi.last().unwrap().get_highest_bit() {
a.copy_from_slice(result_hi);
} else {
limbs_shl_to_out(&mut a, result_hi, 1);
a[0] |= Limb::from(result_lo.last().unwrap().get_highest_bit());
exp_a -= 1;
}
}
Less => {
let err_e;
(exp_a, err_e) = limbs_float_exp(&mut a, abs_base, exp);
exact = err_e == -1;
let two_n = n << 1;
let mut scratch;
let rem;
let result;
let x1 = if two_n <= xs_len {
scratch = vec![0; two_n + 1];
(rem, result) = scratch.split_at_mut(n);
let (xs_lo, xs_hi) = xs.split_at(xs_len - two_n);
if exact && !slice_test_zero(xs_lo) {
exact = false;
}
xs_hi
} else {
scratch = vec![0; (two_n << 1) + 1];
let scratch_2;
(rem, scratch_2) = scratch.split_at_mut(n);
let x1_mut;
(x1_mut, result) = scratch_2.split_at_mut(two_n);
x1_mut[two_n - xs_len..].copy_from_slice(xs);
&*x1_mut
};
if n == 1 {
rem[0] = limbs_div_limb_to_out_mod(result, x1, a[0]);
} else {
limbs_div_mod_to_out(result, rem, x1, &a);
}
exp_a = x_exp - exp_a - i64::exact_from(limb_to_bit_count(two_n));
if exact {
exact = slice_test_zero(rem);
}
let (result_last, result_init) = result.split_last().unwrap();
if *result_last == 1 {
limbs_shr_to_out(&mut a, result_init, 1);
a[n - 1] |= LIMB_HIGH_BIT;
exp_a += 1;
} else {
a.copy_from_slice(result_init);
}
err = if err_e == -1 { 2 } else { i64::from(err_e) + 2 };
}
}
if exact {
err = -1;
}
let mut s = vec![0; digit_len];
assert!(exp_a < 0);
let (ret, e) = limbs_get_str_aux(
&mut s,
&mut a,
exp_a.unsigned_abs(),
err,
base,
digit_len,
rm,
);
match ret {
MPFR_ROUND_FAILED => {
prec += ziv_step;
ziv_step = prec >> 1;
}
NEG_MPFR_ROUND_FAILED => {
if digit_len_i > g {
exp -= 1;
} else {
exp += 1;
}
g += 1;
}
_ => {
return (s, e + g, ret);
}
}
}
}
#[doc(hidden)]
pub fn limbs_get_str_power_of_2(
xs: &[Limb],
x_exp: i64,
x_prec: u64,
abs_base: u64,
base: i64,
digit_len: usize,
rm: RoundingMode,
) -> (Vec<u8>, i64, i8) {
let pow2 = abs_base.significant_bits() - 1; let (mut f, r) = (x_exp - 1).div_mod(i64::exact_from(pow2));
f += 1;
let r = u64::exact_from(r) + 1;
let prec = (u64::exact_from(digit_len) - 1) * pow2 + r;
let len = bit_to_limb_count_ceiling(prec);
let bit_len = limb_to_bit_count(len) - prec;
let mut scratch = vec![0; len + 1];
let (dir, carry) = round_helper_raw(&mut scratch[..len], prec, xs, x_prec, rm);
if carry {
scratch[len - 1] = 0;
scratch[len] = 1;
if r == pow2 {
limbs_slice_shr_in_place(&mut scratch, pow2);
f += 1;
}
}
if bit_len != 0 {
limbs_slice_shr_in_place(&mut scratch, bit_len);
if *scratch.last().unwrap() == 0 {
scratch.pop();
}
}
let digits: Vec<u8> = Natural::from_owned_limbs_asc(scratch).to_power_of_2_digits_desc(pow2);
let num_to_text = if (2..=36).contains(&base) {
NUM_TO_TEXT_36
} else {
NUM_TO_TEXT_62
};
let s = digits[..digit_len]
.iter()
.map(|&d| num_to_text[usize::from(d)])
.collect();
(s, f, dir)
}
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);
}
exp = -(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);
}
exp = 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];
let mut remainder = vec![0; ysize];
if ysize == 1 {
remainder[0] = limbs_div_limb_to_out_mod(&mut quotient, &y0[..2], z[0]);
} else {
limbs_div_mod_to_out(&mut quotient, &mut remainder, &y0[..ysize << 1], &z);
}
assert!(quotient[ysize] <= 1);
err += 1;
exact = exact && slice_test_zero(&remainder);
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,
}
}