use crate::natural::Natural;
use core::cmp::Ordering::*;
use core::ops::Mul;
use malachite_base::num::arithmetic::traits::{CheckedSub, Parity, Pow};
use malachite_base::num::basic::traits::{One, Zero};
use malachite_base::num::conversion::string::from_sci_string::{
cmp_half_helper, is_zero_helper, preprocess_sci_string, validate_helper,
};
use malachite_base::num::conversion::string::options::FromSciStringOptions;
use malachite_base::num::conversion::traits::{FromSciString, FromStringBase};
use malachite_base::rounding_modes::RoundingMode::*;
#[doc(hidden)]
pub trait FromSciStringHelper: Sized {
fn parse_int(cs: &[u8], base: u8) -> Option<Self>;
fn up_1(self, neg: bool) -> Option<Self>;
}
impl FromSciStringHelper for Natural {
fn parse_int(mut cs: &[u8], base: u8) -> Option<Self> {
let mut test_neg_zero = false;
if let Some(&b'-') = cs.first() {
test_neg_zero = true;
}
if test_neg_zero {
if cs.len() == 1 {
return None;
}
for &c in &cs[1..] {
if c != b'0' {
return None;
}
}
Some(Self::ZERO)
} else {
if let Some(b'+') = cs.first() {
cs = &cs[1..];
match cs {
[] | [b'+' | b'-', ..] => return None,
_ => {}
}
}
Self::from_string_base(base, core::str::from_utf8(cs).ok()?)
}
}
fn up_1(self, neg: bool) -> Option<Self> {
if neg {
self.checked_sub(Self::ONE)
} else {
Some(self + Self::ONE)
}
}
}
pub(crate) fn from_sci_string_with_options_helper<
T: From<u8> + FromSciStringHelper + Mul<T, Output = T> + Pow<u64, Output = T> + Zero,
>(
s: &str,
options: FromSciStringOptions,
) -> Option<T>
where
for<'a> &'a T: Parity,
{
let (s, exponent) = preprocess_sci_string(s, options)?;
if exponent >= 0 {
let x = T::parse_int(&s, options.get_base())?;
Some(x * T::from(options.get_base()).pow(exponent.unsigned_abs()))
} else {
let neg_exponent = usize::try_from(exponent.unsigned_abs()).ok()?;
let len = s.len();
if len == 0 {
return None;
}
let first = s[0];
let neg = first == b'-';
let sign = neg || first == b'+';
let rm = if neg {
-options.get_rounding_mode()
} else {
options.get_rounding_mode()
};
let sig_len = if sign { len - 1 } else { len };
if sig_len == 0 {
return None;
}
if neg_exponent > sig_len {
let s = if sign { &s[1..] } else { &s[..] };
return match rm {
Down | Floor | Nearest => {
validate_helper(s, options.get_base())?;
Some(T::ZERO)
}
Up | Ceiling => {
if is_zero_helper(s, options.get_base())? {
Some(T::ZERO)
} else {
T::ZERO.up_1(neg)
}
}
Exact => None,
};
}
let (before_e, after_e) = s.split_at(len - neg_exponent);
let x = match before_e {
&[] | &[b'-'] | &[b'+'] => T::ZERO,
before_e => T::parse_int(before_e, options.get_base())?,
};
if after_e.is_empty() {
return Some(x);
}
match rm {
Down | Floor => {
validate_helper(after_e, options.get_base())?;
Some(x)
}
Up | Ceiling => {
if is_zero_helper(after_e, options.get_base())? {
Some(x)
} else {
x.up_1(neg)
}
}
Exact => {
if is_zero_helper(after_e, options.get_base())? {
Some(x)
} else {
None
}
}
Nearest => match cmp_half_helper(after_e, options.get_base())? {
Less => Some(x),
Greater => x.up_1(neg),
Equal => {
if x.even() {
Some(x)
} else {
x.up_1(neg)
}
}
},
}
}
}
impl FromSciString for Natural {
#[inline]
fn from_sci_string_with_options(s: &str, options: FromSciStringOptions) -> Option<Self> {
from_sci_string_with_options_helper(s, options)
}
}