use crate::Float;
use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::conversion::string::get_str_data::MPFR_L2B;
use crate::floor_and_ceiling;
use alloc::vec;
use alloc::vec::Vec;
use core::cmp::Ordering::{self, Equal};
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{CeilingLogBase2, CheckedLogBase2, NegAssign, Sign};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
use malachite_base::rounding_modes::RoundingMode::{self, Ceiling, Exact, Floor};
use malachite_nz::natural::Natural;
use malachite_nz::natural::arithmetic::float_extras::{limbs_get_str, limbs_get_str_power_of_2};
pub_crate_test! {ceil_mul(e: i64, beta: u64, i: usize) -> i64 {
const WIDTH_MINUS_1: u64 = i64::WIDTH - 1;
let (mantissa, exp) = MPFR_L2B[usize::exact_from(beta) - 2][i];
let p = Float::from_natural_prec(Natural::from(mantissa), 128).0
>> u64::exact_from(128 - i64::from(exp));
let t = Float::from_signed_prec_round(e, WIDTH_MINUS_1, Ceiling)
.0
.mul_prec_round(p, WIDTH_MINUS_1, Ceiling)
.0;
i64::rounding_from(&t, Ceiling).0
}}
pub_crate_test! {
get_str_ndigits(base: u64, bit_len: u64) -> usize {
assert!((2..=62).contains(&base));
if let Some(k) = base.checked_log_base_2() {
return usize::exact_from(1 + (bit_len + k - 2) / k);
}
let ret = if bit_len < 186_564_318_007 {
u64::exact_from(ceil_mul(i64::exact_from(bit_len), base, 1))
} else {
fail_on_untested_path("get_str_ndigits, Ziv loop for huge bit_len");
let mut w = 77;
loop {
w <<= 1;
let (log_lo, log_hi) =
floor_and_ceiling(Float::from_unsigned_prec(base, w).0.log_base_2_round(Floor));
let pf = Float::from_unsigned_prec(bit_len, w).0;
let lo = u64::rounding_from(&pf.div_round_ref_val(log_hi, Floor).0, Ceiling).0;
let hi = u64::rounding_from(&pf.div_round(log_lo, Ceiling).0, Ceiling).0;
if lo == hi {
break lo;
}
}
};
usize::exact_from(1 + ret)
}}
pub fn get_str(
x: &Float,
base: i64,
digit_len: usize,
mut rm: RoundingMode,
) -> Option<(Vec<u8>, i64, Ordering)> {
if !(-36..=-2).contains(&base) && !(2..=62).contains(&base) {
return None;
}
let b = base.unsigned_abs();
let (neg, mut s, e, dir) = match &x.0 {
NaN => return Some((b"@NaN@".to_vec(), 0, Equal)),
Infinity { sign } => {
let s = if *sign {
b"@Inf@".to_vec()
} else {
b"-@Inf@".to_vec()
};
return Some((s, 0, Equal));
}
Zero { sign } => {
(
!*sign,
vec![b'0'; if digit_len == 0 { 1 } else { digit_len }],
0,
0,
)
}
Finite {
sign,
exponent,
precision,
significand,
} => {
let m = if digit_len == 0 {
get_str_ndigits(b, *precision)
} else {
digit_len
};
let neg = !*sign;
if neg {
rm.neg_assign();
}
let xp = significand.to_limbs_asc();
let x_exp = i64::from(*exponent);
let (s, e, dir) = if b.is_power_of_two() {
limbs_get_str_power_of_2(&xp, x_exp, *precision, b, base, m, rm)
} else {
let g = ceil_mul(x_exp - 1, b, 1);
let exp = (i64::exact_from(m) - g).unsigned_abs();
let mut prec = u64::exact_from(ceil_mul(i64::exact_from(m), b, 0)) + 1;
prec += prec.ceiling_log_base_2();
if exp != 0 {
prec += 3 * exp.ceiling_log_base_2();
}
limbs_get_str(&xp, x_exp, b, base, m, rm, g, prec, i64::exact_from(exp))
};
(neg, s, e, dir)
}
};
assert!(
rm != Exact || dir == 0,
"get_str: Exact rounding was requested, but {x} is not exactly representable in the \
requested number of base-{base} digits"
);
let o = dir.sign();
Some(if neg {
s.insert(0, b'-');
(s, e, o.reverse())
} else {
(s, e, o)
})
}