use crate::Float;
use crate::float::conversion::string::get_str::ceil_mul;
use crate::float::conversion::string::set_str::{overflow, set_str_helper};
use alloc::borrow::Cow;
use alloc::format;
use alloc::vec::Vec;
use core::cmp::Ordering::{self, *};
use core::str::FromStr;
use malachite_base::num::basic::traits::{Infinity, NaN, NegativeInfinity, NegativeZero, Zero};
use malachite_base::num::conversion::string::from_sci_string::preprocess_sci_string;
use malachite_base::num::conversion::string::from_string::digit_from_display_byte;
use malachite_base::num::conversion::string::options::FromSciStringOptions;
use malachite_base::num::conversion::traits::{
ExactFrom, FromSciString, IntegerMantissaAndExponent,
};
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
enum Parsed {
Special(Float),
Zero(bool),
Finite(bool, Vec<u8>, i64, usize),
Overflow(bool),
}
fn parse(s: &str, options: FromSciStringOptions) -> Option<Parsed> {
match s {
"NaN" => return Some(Parsed::Special(Float::NAN)),
"Infinity" => return Some(Parsed::Special(Float::INFINITY)),
"-Infinity" => return Some(Parsed::Special(Float::NEGATIVE_INFINITY)),
_ => {}
}
let base = options.get_base();
let (chars, exponent) = preprocess_sci_string(s, options)?;
let (sign, chars) = match chars.split_first() {
Some((&b'-', rest)) => (false, rest),
Some((&b'+', rest)) => (true, rest),
_ => (true, &chars[..]),
};
if chars.is_empty() {
return None;
}
let mut digits = Vec::with_capacity(chars.len());
for &c in chars {
let digit = digit_from_display_byte(c)?;
if digit >= base {
return None;
}
digits.push(digit);
}
let Some(mut exp_base) = i64::exact_from(digits.len()).checked_add(exponent) else {
return Some(Parsed::Overflow(sign));
};
let leading = digits.iter().take_while(|&&d| d == 0).count();
digits.drain(..leading);
exp_base -= i64::exact_from(leading);
let significant = digits.len();
while digits.last() == Some(&0) {
digits.pop();
}
Some(if digits.is_empty() {
Parsed::Zero(sign)
} else {
Parsed::Finite(sign, digits, exp_base, significant)
})
}
fn implied_prec(significant: usize, base: u8) -> u64 {
u64::exact_from(ceil_mul(i64::exact_from(significant), u64::from(base), 0))
}
impl Float {
pub fn from_sci_string_prec_round(
s: &str,
prec: u64,
rm: RoundingMode,
) -> Option<(Self, Ordering)> {
let mut options = FromSciStringOptions::default();
options.set_rounding_mode(rm);
Self::from_sci_string_with_options_prec(s, options, prec)
}
#[inline]
pub fn from_sci_string_prec(s: &str, prec: u64) -> Option<(Self, Ordering)> {
Self::from_sci_string_with_options_prec(s, FromSciStringOptions::default(), prec)
}
pub fn from_sci_string_with_options_prec(
s: &str,
options: FromSciStringOptions,
prec: u64,
) -> Option<(Self, Ordering)> {
assert_ne!(prec, 0);
let rm = options.get_rounding_mode();
Some(match parse(s, options)? {
Parsed::Special(x) => (x, Equal),
Parsed::Zero(sign) => (
if sign {
Self::ZERO
} else {
Self::NEGATIVE_ZERO
},
Equal,
),
Parsed::Overflow(sign) => overflow(sign, prec, rm),
Parsed::Finite(sign, digits, exp_base, _) => {
set_str_helper(sign, &digits, options.get_base(), exp_base, 0, prec, rm)
}
})
}
}
const fn base_prefix(base: u8) -> &'static str {
match base {
2 => "0b",
8 => "0o",
16 => "0x",
_ => "",
}
}
pub_crate_test! {
float_from_string_base(base: u8, s: &str) -> Option<Float> {
let mut options = FromSciStringOptions::default();
options.set_base(base);
let (body, prec) = match s.rfind('#') {
Some(i) => (&s[..i], Some(u64::from_str(&s[i + 1..]).ok()?)),
None => (s, None),
};
if prec == Some(0) {
return None;
}
let (sign, after_sign) = match body.strip_prefix('-') {
Some(rest) => ("-", rest),
None => ("", body),
};
let prefix = base_prefix(base);
let digits = if prefix.is_empty() {
after_sign
} else {
after_sign.strip_prefix(prefix).unwrap_or(after_sign)
};
let rebuilt = if sign.is_empty() && prefix.is_empty() {
Cow::Borrowed(body)
} else {
Cow::Owned(format!("{sign}{digits}"))
};
match prec {
Some(prec) => {
let x = Float::from_sci_string_with_options_prec(&rebuilt, options, prec)?.0;
x.get_prec().map(|_| x)
}
None => Float::from_sci_string_with_options(&rebuilt, options),
}
}}
impl FromSciString for Float {
fn from_sci_string_with_options(s: &str, options: FromSciStringOptions) -> Option<Self> {
let base = options.get_base();
Some(match parse(s, options)? {
Parsed::Special(x) => x,
Parsed::Zero(sign) => {
if sign {
Self::ZERO
} else {
Self::NEGATIVE_ZERO
}
}
Parsed::Overflow(sign) => {
if sign {
Self::INFINITY
} else {
Self::NEGATIVE_INFINITY
}
}
Parsed::Finite(sign, digits, exp_base, significant) => {
let prec = implied_prec(significant, base);
let (x, o) = set_str_helper(sign, &digits, base, exp_base, 0, prec, Nearest);
if o == Equal {
let min_prec = (&x).integer_mantissa().significant_bits();
Self::from_float_prec_round(x, min_prec, Exact).0
} else {
x
}
}
})
}
}