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::{
MPFR_ROUND_FAILED, NEG_MPFR_ROUND_FAILED, round_helper_2, round_helper_raw,
round_helper_raw_aliased,
};
use crate::natural::arithmetic::mul::limbs_mul;
use crate::natural::arithmetic::shl::limbs_shl_to_out;
use crate::natural::arithmetic::shr::{limbs_shr_to_out, limbs_slice_shr_in_place};
use crate::natural::conversion::digits::general_digits::limbs_to_digits_small_base;
use crate::natural::{
LIMB_HIGH_BIT, Natural, bit_to_limb_count_ceiling, bit_to_limb_count_floor, limb_to_bit_count,
};
use crate::platform::Limb;
use alloc::vec::Vec;
use core::cmp::Ordering::*;
use malachite_base::num::arithmetic::traits::{DivMod, Parity};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::conversion::traits::{ExactFrom, PowerOf2Digits};
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_base::slices::slice_test_zero;
const NUM_TO_TEXT_36: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
const NUM_TO_TEXT_62: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private_test_fn! {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)
}