use bigdecimal::{
BigDecimal,
num_bigint::{BigInt, BigUint, Sign},
};
use num_traits::Signed;
use num_traits::ToPrimitive;
use num_traits::Zero;
use crate::extendedbigdecimal::ExtendedBigDecimal;
#[derive(Clone, Copy, PartialEq)]
enum Base {
Binary = 2,
Octal = 8,
Decimal = 10,
Hexadecimal = 16,
}
impl Base {
fn digit(self, c: char) -> Option<u64> {
fn from_decimal(c: char) -> u64 {
u64::from(c) - u64::from('0')
}
match self {
Self::Binary => ('0'..='1').contains(&c).then(|| from_decimal(c)),
Self::Octal => ('0'..='7').contains(&c).then(|| from_decimal(c)),
Self::Decimal => c.is_ascii_digit().then(|| from_decimal(c)),
Self::Hexadecimal => match c.to_ascii_lowercase() {
'0'..='9' => Some(from_decimal(c)),
c @ 'a'..='f' => Some(u64::from(c) - u64::from('a') + 10),
_ => None,
},
}
}
fn parse_digits(self, str: &str) -> (Option<BigUint>, &str) {
let (digits, _, rest) = self.parse_digits_count(str, None);
(digits, rest)
}
fn parse_digits_count(
self,
str: &str,
digits: Option<BigUint>,
) -> (Option<BigUint>, i64, &str) {
let mut digits: Option<BigUint> = digits;
let mut count: i64 = 0;
let mut rest = str;
let mut digits_tmp: u64 = 0;
let mut count_tmp: i64 = 0;
let mut mul_tmp: u64 = 1;
while let Some(d) = rest.chars().next().and_then(|c| self.digit(c)) {
(digits_tmp, count_tmp, mul_tmp) = (
digits_tmp * self as u64 + d,
count_tmp + 1,
mul_tmp * self as u64,
);
rest = &rest[1..];
if count_tmp >= 15 {
(digits, count) = (
Some(digits.unwrap_or_default() * mul_tmp + digits_tmp),
count + count_tmp,
);
(digits_tmp, count_tmp, mul_tmp) = (0, 0, 1);
}
}
if mul_tmp > 1 {
(digits, count) = (
Some(digits.unwrap_or_default() * mul_tmp + digits_tmp),
count + count_tmp,
);
}
(digits, count, rest)
}
}
#[derive(Debug, PartialEq)]
pub enum ExtendedParserError<T> {
NotNumeric,
PartialMatch(T, String),
Overflow(T),
Underflow(T),
}
impl<T> ExtendedParserError<T>
where
T: Zero,
{
fn extract(self) -> T {
match self {
Self::NotNumeric => T::zero(),
Self::PartialMatch(v, _) => v,
Self::Overflow(v) => v,
Self::Underflow(v) => v,
}
}
fn map<U>(
self,
f: impl FnOnce(T) -> Result<U, ExtendedParserError<U>>,
) -> ExtendedParserError<U>
where
U: Zero,
{
fn extract<U>(v: Result<U, ExtendedParserError<U>>) -> U
where
U: Zero,
{
v.unwrap_or_else(ExtendedParserError::extract)
}
match self {
Self::NotNumeric => ExtendedParserError::NotNumeric,
Self::PartialMatch(v, rest) => ExtendedParserError::PartialMatch(extract(f(v)), rest),
Self::Overflow(v) => ExtendedParserError::Overflow(extract(f(v))),
Self::Underflow(v) => ExtendedParserError::Underflow(extract(f(v))),
}
}
}
pub trait ExtendedParser {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>>
where
Self: Sized;
}
impl ExtendedParser for i64 {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>> {
fn into_i64(ebd: ExtendedBigDecimal) -> Result<i64, ExtendedParserError<i64>> {
match ebd {
ExtendedBigDecimal::BigDecimal(bd) => {
let (digits, scale) = bd.into_bigint_and_scale();
if scale == 0 {
let negative = digits.sign() == Sign::Minus;
match i64::try_from(digits) {
Ok(i) => Ok(i),
_ => Err(ExtendedParserError::Overflow(if negative {
i64::MIN
} else {
i64::MAX
})),
}
} else {
Err(ExtendedParserError::NotNumeric)
}
}
ExtendedBigDecimal::MinusZero => Ok(0),
_ => Err(ExtendedParserError::NotNumeric),
}
}
match parse(input, ParseTarget::Integral, &[]) {
Ok(v) => into_i64(v),
Err(e) => Err(e.map(into_i64)),
}
}
}
impl ExtendedParser for u64 {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>> {
fn into_u64(ebd: ExtendedBigDecimal) -> Result<u64, ExtendedParserError<u64>> {
match ebd {
ExtendedBigDecimal::BigDecimal(bd) => {
let (digits, scale) = bd.into_bigint_and_scale();
if scale == 0 {
let (sign, digits) = digits.into_parts();
match u64::try_from(digits) {
Ok(i) => {
if sign == Sign::Minus {
Ok(!i + 1)
} else {
Ok(i)
}
}
_ => Err(ExtendedParserError::Overflow(u64::MAX)),
}
} else {
Err(ExtendedParserError::NotNumeric)
}
}
ExtendedBigDecimal::MinusZero => Ok(0),
_ => Err(ExtendedParserError::NotNumeric),
}
}
match parse(input, ParseTarget::Integral, &[]) {
Ok(v) => into_u64(v),
Err(e) => Err(e.map(into_u64)),
}
}
}
impl ExtendedParser for f64 {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>> {
fn into_f64(ebd: ExtendedBigDecimal) -> Result<f64, ExtendedParserError<f64>> {
let v = match ebd {
ExtendedBigDecimal::BigDecimal(bd) => {
let f = bd.to_f64().unwrap();
if f.is_infinite() {
return Err(ExtendedParserError::Overflow(f));
}
if f.is_zero() && !bd.is_zero() {
return Err(ExtendedParserError::Underflow(f));
}
f
}
ExtendedBigDecimal::MinusZero => -0.0,
ExtendedBigDecimal::Nan => f64::NAN,
ExtendedBigDecimal::MinusNan => -f64::NAN,
ExtendedBigDecimal::Infinity => f64::INFINITY,
ExtendedBigDecimal::MinusInfinity => -f64::INFINITY,
};
Ok(v)
}
match parse(input, ParseTarget::Decimal, &[]) {
Ok(v) => into_f64(v),
Err(e) => Err(e.map(into_f64)),
}
}
}
impl ExtendedParser for ExtendedBigDecimal {
fn extended_parse(input: &str) -> Result<Self, ExtendedParserError<Self>> {
parse(input, ParseTarget::Decimal, &[])
}
}
fn parse_digits(base: Base, str: &str, fractional: bool) -> (Option<BigUint>, i64, &str) {
let (digits, rest) = base.parse_digits(str);
if fractional {
if let Some(rest) = rest.strip_prefix('.') {
return base.parse_digits_count(rest, digits);
}
}
(digits, 0, rest)
}
fn parse_exponent(base: Base, str: &str) -> (Option<BigInt>, &str) {
let exp_chars = match base {
Base::Decimal => ['e', 'E'],
Base::Hexadecimal => ['p', 'P'],
_ => unreachable!(),
};
if let Some(rest) = str.strip_prefix(exp_chars) {
let (sign, rest) = if let Some(rest) = rest.strip_prefix('-') {
(Sign::Minus, rest)
} else if let Some(rest) = rest.strip_prefix('+') {
(Sign::Plus, rest)
} else {
(Sign::Plus, rest) };
let (exp_uint, rest) = Base::Decimal.parse_digits(rest);
if let Some(exp_uint) = exp_uint {
return (Some(BigInt::from_biguint(sign, exp_uint)), rest);
}
}
(None, str)
}
fn parse_suffix_multiplier<'a>(str: &'a str, allowed_suffixes: &[(char, u32)]) -> (u32, &'a str) {
if let Some(ch) = str.chars().next() {
if let Some(mul) = allowed_suffixes
.iter()
.find_map(|(c, t)| (ch == *c).then_some(*t))
{
return (mul, &str[1..]);
}
}
(1, str)
}
fn parse_special_value(
input: &str,
negative: bool,
allowed_suffixes: &[(char, u32)],
) -> Result<ExtendedBigDecimal, ExtendedParserError<ExtendedBigDecimal>> {
let input_lc = input.to_ascii_lowercase();
const MATCH_TABLE: &[(&str, ExtendedBigDecimal)] = &[
("infinity", ExtendedBigDecimal::Infinity),
("inf", ExtendedBigDecimal::Infinity),
("nan", ExtendedBigDecimal::Nan),
];
for (str, ebd) in MATCH_TABLE {
if input_lc.starts_with(str) {
let mut special = ebd.clone();
if negative {
special = -special;
}
let (_, rest) = parse_suffix_multiplier(&input[str.len()..], allowed_suffixes);
return if rest.is_empty() {
Ok(special)
} else {
Err(ExtendedParserError::PartialMatch(special, rest.to_string()))
};
}
}
Err(ExtendedParserError::NotNumeric)
}
fn make_error(overflow: bool, negative: bool) -> ExtendedParserError<ExtendedBigDecimal> {
let mut v = if overflow {
ExtendedBigDecimal::Infinity
} else {
ExtendedBigDecimal::zero()
};
if negative {
v = -v;
}
if overflow {
ExtendedParserError::Overflow(v)
} else {
ExtendedParserError::Underflow(v)
}
}
fn construct_extended_big_decimal(
digits: BigUint,
negative: bool,
base: Base,
scale: i64,
exponent: BigInt,
) -> Result<ExtendedBigDecimal, ExtendedParserError<ExtendedBigDecimal>> {
if digits == BigUint::zero() {
return Ok(if negative {
ExtendedBigDecimal::MinusZero
} else {
ExtendedBigDecimal::zero()
});
}
let sign = if negative { Sign::Minus } else { Sign::Plus };
let signed_digits = BigInt::from_biguint(sign, digits);
let bd = if scale == 0 && exponent.is_zero() {
BigDecimal::from_bigint(signed_digits, 0)
} else if base == Base::Decimal {
if exponent.is_zero() {
BigDecimal::from_bigint(signed_digits, scale)
} else {
let new_scale = -exponent + scale;
if let Some(new_scale) = new_scale.to_i64() {
BigDecimal::from_bigint(signed_digits, new_scale)
} else {
return Err(make_error(new_scale.is_negative(), negative));
}
}
} else if base == Base::Hexadecimal {
if scale > u32::MAX.into() {
return Err(ExtendedParserError::NotNumeric);
}
let bd = BigDecimal::from_bigint(signed_digits, 0)
/ BigDecimal::from_bigint(BigInt::from(16).pow(scale as u32), 0);
let Some(exponent) = exponent.to_i64() else {
return Err(make_error(exponent.is_positive(), negative));
};
let base: BigDecimal = 2.into();
let pow2 = base.powi(exponent);
bd * pow2
} else {
unreachable!();
};
Ok(ExtendedBigDecimal::BigDecimal(bd))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ParseTarget {
Decimal,
Integral,
Duration,
}
pub(crate) fn parse(
input: &str,
target: ParseTarget,
allowed_suffixes: &[(char, u32)],
) -> Result<ExtendedBigDecimal, ExtendedParserError<ExtendedBigDecimal>> {
let trimmed_input = input.trim_ascii_start();
let (negative, unsigned) = if let Some(trimmed_input) = trimmed_input.strip_prefix('-') {
(true, trimmed_input)
} else if let Some(trimmed_input) = trimmed_input.strip_prefix('+') {
(false, trimmed_input)
} else {
(false, trimmed_input)
};
let (base, rest) = if let Some(rest) = unsigned.strip_prefix('0') {
if let Some(rest) = rest.strip_prefix(['x', 'X']) {
(Base::Hexadecimal, rest)
} else if target == ParseTarget::Integral {
if let Some(rest) = rest.strip_prefix(['b', 'B']) {
(Base::Binary, rest)
} else {
(Base::Octal, unsigned)
}
} else {
(Base::Decimal, unsigned)
}
} else {
(Base::Decimal, unsigned)
};
let parse_frac_exp =
matches!(base, Base::Decimal | Base::Hexadecimal) && target != ParseTarget::Integral;
let (digits, scale, rest) = parse_digits(base, rest, parse_frac_exp);
let (exponent, rest) = if parse_frac_exp {
parse_exponent(base, rest)
} else {
(None, rest)
};
if digits.is_none() {
if let Some(partial) = unsigned.strip_prefix("0") {
let ebd = if negative {
ExtendedBigDecimal::MinusZero
} else {
ExtendedBigDecimal::zero()
};
return Err(ExtendedParserError::PartialMatch(ebd, partial.to_string()));
}
return if target == ParseTarget::Integral {
Err(ExtendedParserError::NotNumeric)
} else {
parse_special_value(unsigned, negative, allowed_suffixes)
};
}
let (mul, rest) = parse_suffix_multiplier(rest, allowed_suffixes);
let digits = digits.unwrap() * mul;
let ebd_result =
construct_extended_big_decimal(digits, negative, base, scale, exponent.unwrap_or_default());
if rest.is_empty() {
ebd_result
} else {
Err(ExtendedParserError::PartialMatch(
ebd_result.unwrap_or_else(ExtendedParserError::extract),
rest.to_string(),
))
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use bigdecimal::BigDecimal;
use crate::extendedbigdecimal::ExtendedBigDecimal;
use super::{ExtendedParser, ExtendedParserError};
#[test]
fn test_decimal_u64() {
assert_eq!(Ok(123), u64::extended_parse("123"));
assert_eq!(Ok(u64::MAX), u64::extended_parse(&format!("{}", u64::MAX)));
assert_eq!(Ok(0), u64::extended_parse("-0"));
assert_eq!(Ok(u64::MAX), u64::extended_parse("-1"));
assert_eq!(
Ok(u64::MAX / 2 + 1),
u64::extended_parse("-9223372036854775808") );
assert_eq!(
Ok(1_123_372_036_854_675_616),
u64::extended_parse("-17323372036854876000") );
assert_eq!(Ok(1), u64::extended_parse("-18446744073709551615")); assert!(matches!(
u64::extended_parse("-18446744073709551616"), Err(ExtendedParserError::Overflow(u64::MAX))
));
assert!(matches!(
u64::extended_parse("-92233720368547758150"),
Err(ExtendedParserError::Overflow(u64::MAX))
));
assert!(matches!(
u64::extended_parse("-170141183460469231731687303715884105729"),
Err(ExtendedParserError::Overflow(u64::MAX))
));
assert!(matches!(
u64::extended_parse(""),
Err(ExtendedParserError::NotNumeric)
));
assert_eq!(
u64::extended_parse("123.15"),
Err(ExtendedParserError::PartialMatch(123, ".15".to_string()))
);
assert_eq!(
u64::extended_parse("123e10"),
Err(ExtendedParserError::PartialMatch(123, "e10".to_string()))
);
}
#[test]
fn test_decimal_i64() {
assert_eq!(Ok(123), i64::extended_parse("123"));
assert_eq!(Ok(123), i64::extended_parse("+123"));
assert_eq!(Ok(-123), i64::extended_parse("-123"));
assert!(matches!(
i64::extended_parse("--123"),
Err(ExtendedParserError::NotNumeric)
));
assert_eq!(Ok(i64::MAX), i64::extended_parse(&format!("{}", i64::MAX)));
assert_eq!(Ok(i64::MIN), i64::extended_parse(&format!("{}", i64::MIN)));
assert_eq!(
i64::extended_parse(&format!("{}", u64::MAX)),
Err(ExtendedParserError::Overflow(i64::MAX))
);
assert!(matches!(
i64::extended_parse(&format!("{}", i64::MAX as u64 + 1)),
Err(ExtendedParserError::Overflow(i64::MAX))
));
assert_eq!(
i64::extended_parse("-123e10"),
Err(ExtendedParserError::PartialMatch(-123, "e10".to_string()))
);
assert!(matches!(
i64::extended_parse(&format!("{}", -(u64::MAX as i128))),
Err(ExtendedParserError::Overflow(i64::MIN))
));
assert!(matches!(
i64::extended_parse(&format!("{}", i64::MIN as i128 - 1)),
Err(ExtendedParserError::Overflow(i64::MIN))
));
assert!(matches!(
i64::extended_parse(""),
Err(ExtendedParserError::NotNumeric)
));
assert!(matches!(
i64::extended_parse("."),
Err(ExtendedParserError::NotNumeric)
));
}
#[test]
fn test_decimal_f64() {
assert_eq!(Ok(123.0), f64::extended_parse("123"));
assert_eq!(Ok(123.0), f64::extended_parse("+123"));
assert_eq!(Ok(-123.0), f64::extended_parse("-123"));
assert_eq!(Ok(123.0), f64::extended_parse("123."));
assert_eq!(Ok(-123.0), f64::extended_parse("-123."));
assert_eq!(Ok(123.0), f64::extended_parse("123.0"));
assert_eq!(Ok(-123.0), f64::extended_parse("-123.0"));
assert_eq!(Ok(123.15), f64::extended_parse("123.15"));
assert_eq!(Ok(123.15), f64::extended_parse("+123.15"));
assert_eq!(Ok(-123.15), f64::extended_parse("-123.15"));
assert_eq!(Ok(0.15), f64::extended_parse(".15"));
assert_eq!(Ok(-0.15), f64::extended_parse("-.15"));
assert_eq!(Ok(123.0), f64::extended_parse("0123"));
assert_eq!(Ok(123.0), f64::extended_parse("+0123"));
assert_eq!(Ok(-123.0), f64::extended_parse("-0123"));
assert_eq!(Ok(123.0), f64::extended_parse("00123"));
assert_eq!(Ok(123.0), f64::extended_parse("+00123"));
assert_eq!(Ok(-123.0), f64::extended_parse("-00123"));
assert_eq!(Ok(123.15), f64::extended_parse("0123.15"));
assert_eq!(Ok(123.15), f64::extended_parse("+0123.15"));
assert_eq!(Ok(-123.15), f64::extended_parse("-0123.15"));
assert_eq!(Ok(12_315_000.0), f64::extended_parse("123.15e5"));
assert_eq!(Ok(-12_315_000.0), f64::extended_parse("-123.15e5"));
assert_eq!(Ok(12_315_000.0), f64::extended_parse("123.15E+5"));
assert_eq!(Ok(0.001_231_5), f64::extended_parse("123.15E-5"));
assert_eq!(
Ok(0.15),
f64::extended_parse(".150000000000000000000000000231313")
);
assert_eq!(
f64::extended_parse("123.15e"),
Err(ExtendedParserError::PartialMatch(123.15, "e".to_string()))
);
assert_eq!(
f64::extended_parse("123.15E"),
Err(ExtendedParserError::PartialMatch(123.15, "E".to_string()))
);
assert_eq!(
f64::extended_parse("123.15e-"),
Err(ExtendedParserError::PartialMatch(123.15, "e-".to_string()))
);
assert_eq!(
f64::extended_parse("123.15e+"),
Err(ExtendedParserError::PartialMatch(123.15, "e+".to_string()))
);
assert_eq!(
f64::extended_parse("123.15e."),
Err(ExtendedParserError::PartialMatch(123.15, "e.".to_string()))
);
assert_eq!(
f64::extended_parse("1.2.3"),
Err(ExtendedParserError::PartialMatch(1.2, ".3".to_string()))
);
assert_eq!(
f64::extended_parse("123.15p5"),
Err(ExtendedParserError::PartialMatch(123.15, "p5".to_string()))
);
assert_eq!(Ok(0.0), f64::extended_parse("-0.0"));
assert!(f64::extended_parse("-0.0").unwrap().is_sign_negative());
assert_eq!(Ok(f64::INFINITY), f64::extended_parse("inf"));
assert_eq!(Ok(f64::INFINITY), f64::extended_parse("+inf"));
assert_eq!(Ok(f64::NEG_INFINITY), f64::extended_parse("-inf"));
assert_eq!(Ok(f64::INFINITY), f64::extended_parse("Inf"));
assert_eq!(Ok(f64::INFINITY), f64::extended_parse("InF"));
assert_eq!(Ok(f64::INFINITY), f64::extended_parse("INF"));
assert_eq!(Ok(f64::INFINITY), f64::extended_parse("infinity"));
assert_eq!(Ok(f64::INFINITY), f64::extended_parse("+infiNIty"));
assert_eq!(Ok(f64::NEG_INFINITY), f64::extended_parse("-INfinity"));
assert!(f64::extended_parse("NaN").unwrap().is_nan());
assert!(f64::extended_parse("NaN").unwrap().is_sign_positive());
assert!(f64::extended_parse("+NaN").unwrap().is_nan());
assert!(f64::extended_parse("+NaN").unwrap().is_sign_positive());
assert!(f64::extended_parse("-NaN").unwrap().is_nan());
assert!(f64::extended_parse("-NaN").unwrap().is_sign_negative());
assert!(f64::extended_parse("nan").unwrap().is_nan());
assert!(f64::extended_parse("nan").unwrap().is_sign_positive());
assert!(f64::extended_parse("NAN").unwrap().is_nan());
assert!(f64::extended_parse("NAN").unwrap().is_sign_positive());
assert_eq!(
f64::extended_parse("-infinit"),
Err(ExtendedParserError::PartialMatch(
f64::NEG_INFINITY,
"init".to_string()
))
);
assert_eq!(
f64::extended_parse("-infinity00"),
Err(ExtendedParserError::PartialMatch(
f64::NEG_INFINITY,
"00".to_string()
))
);
assert!(f64::extended_parse(&format!("{}", u64::MAX)).is_ok());
assert!(f64::extended_parse(&format!("{}", i64::MIN)).is_ok());
assert!(matches!(
f64::extended_parse("1.0e9000"),
Err(ExtendedParserError::Overflow(f64::INFINITY))
));
assert!(matches!(
f64::extended_parse("-10.0e9000"),
Err(ExtendedParserError::Overflow(f64::NEG_INFINITY))
));
assert!(matches!(
f64::extended_parse("1.0e-9000"),
Err(ExtendedParserError::Underflow(0.0))
));
assert!(matches!(
f64::extended_parse("-1.0e-9000"),
Err(ExtendedParserError::Underflow(f)) if f == 0.0 && f.is_sign_negative()));
}
#[test]
fn test_decimal_extended_big_decimal() {
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(
BigDecimal::from_str("123").unwrap()
)),
ExtendedBigDecimal::extended_parse("123")
);
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(
BigDecimal::from_str("123.15").unwrap()
)),
ExtendedBigDecimal::extended_parse("123.15")
);
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(BigDecimal::from_bigint(
12315.into(),
-98
))),
ExtendedBigDecimal::extended_parse("123.15e100")
);
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(BigDecimal::from_bigint(
12315.into(),
102
))),
ExtendedBigDecimal::extended_parse("123.15E-100")
);
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(
BigDecimal::from_str(".150000000000000000000000000000000000001").unwrap()
)),
ExtendedBigDecimal::extended_parse(".150000000000000000000000000000000000001")
);
assert!(matches!(
ExtendedBigDecimal::extended_parse("nan"),
Ok(ExtendedBigDecimal::Nan)
));
assert!(matches!(
ExtendedBigDecimal::extended_parse("-NAN"),
Ok(ExtendedBigDecimal::MinusNan)
));
assert_eq!(
Ok(ExtendedBigDecimal::Infinity),
ExtendedBigDecimal::extended_parse("InF")
);
assert_eq!(
Ok(ExtendedBigDecimal::MinusInfinity),
ExtendedBigDecimal::extended_parse("-iNf")
);
assert_eq!(
Ok(ExtendedBigDecimal::zero()),
ExtendedBigDecimal::extended_parse("0.0")
);
assert!(matches!(
ExtendedBigDecimal::extended_parse("-0.0"),
Ok(ExtendedBigDecimal::MinusZero)
));
assert!(matches!(
ExtendedBigDecimal::extended_parse(&format!("1e{}", i64::MAX as u64 + 2)),
Err(ExtendedParserError::Overflow(ExtendedBigDecimal::Infinity))
));
assert!(matches!(
ExtendedBigDecimal::extended_parse(&format!("-0.1e{}", i64::MAX as u64 + 3)),
Err(ExtendedParserError::Overflow(
ExtendedBigDecimal::MinusInfinity
))
));
assert!(matches!(
ExtendedBigDecimal::extended_parse(&format!("1e{}", i64::MIN)),
Err(ExtendedParserError::Underflow(ebd)) if ebd == ExtendedBigDecimal::zero()
));
assert!(matches!(
ExtendedBigDecimal::extended_parse(&format!("-0.01e{}", i64::MIN + 2)),
Err(ExtendedParserError::Underflow(
ExtendedBigDecimal::MinusZero
))
));
assert_eq!(
ExtendedBigDecimal::extended_parse(&format!("0e{}", i64::MAX as u64 + 2)),
Ok(ExtendedBigDecimal::zero()),
);
assert_eq!(
ExtendedBigDecimal::extended_parse(&format!("-0.0e{}", i64::MAX as u64 + 3)),
Ok(ExtendedBigDecimal::MinusZero)
);
assert_eq!(
ExtendedBigDecimal::extended_parse(&format!("0.0000e{}", i64::MIN)),
Ok(ExtendedBigDecimal::zero()),
);
assert_eq!(
ExtendedBigDecimal::extended_parse(&format!("-0e{}", i64::MIN + 2)),
Ok(ExtendedBigDecimal::MinusZero)
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse(".")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("e")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse(".e")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("-e")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("+.e")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("e10")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("e-10")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("-e10")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
ExtendedBigDecimal::extended_parse("+e10")
);
}
#[test]
fn test_hexadecimal() {
assert_eq!(Ok(0x123), u64::extended_parse("0x123"));
assert_eq!(Ok(0x123), u64::extended_parse("0X123"));
assert_eq!(Ok(0x123), u64::extended_parse("+0x123"));
assert_eq!(Ok(0xfe), u64::extended_parse("0xfE"));
assert_eq!(Ok(-0x123), i64::extended_parse("-0x123"));
assert_eq!(Ok(0.5), f64::extended_parse("0x.8"));
assert_eq!(Ok(0.0625), f64::extended_parse("0x.1"));
assert_eq!(Ok(15.007_812_5), f64::extended_parse("0xf.02"));
assert_eq!(Ok(16.0), f64::extended_parse("0x0.8p5"));
assert_eq!(Ok(0.0625), f64::extended_parse("0x1P-4"));
assert_eq!(Ok(0.555_908_203_125), f64::extended_parse("0x0.8e5"));
assert_eq!(
f64::extended_parse("0x0.1p"),
Err(ExtendedParserError::PartialMatch(0.0625, "p".to_string()))
);
assert_eq!(
f64::extended_parse("0x0.1p-"),
Err(ExtendedParserError::PartialMatch(0.0625, "p-".to_string()))
);
assert_eq!(
f64::extended_parse("0x.1p+"),
Err(ExtendedParserError::PartialMatch(0.0625, "p+".to_string()))
);
assert_eq!(
f64::extended_parse("0x.1p."),
Err(ExtendedParserError::PartialMatch(0.0625, "p.".to_string()))
);
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(
BigDecimal::from_str("0.0625").unwrap()
)),
ExtendedBigDecimal::extended_parse("0x.1")
);
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(
BigDecimal::from_str("15.999999999999999999999999948301211715435770320536956745627321652136743068695068359375").unwrap()
)),
ExtendedBigDecimal::extended_parse("0xf.fffffffffffffffffffff")
);
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(
BigDecimal::from_str("9.816204233623505350831385407878283564899139328691307267002649220552261820356883420275966921502700387e+903089986").unwrap()
)),
ExtendedBigDecimal::extended_parse("0x1p3000000000")
);
assert_eq!(
Ok(ExtendedBigDecimal::BigDecimal(
BigDecimal::from_str("1.349213146236998355103608893554488871595951104574239597804963176857050954139054064644219311222652032e-9030900").unwrap()
)),
ExtendedBigDecimal::extended_parse("0x1p-30000000")
);
assert!(matches!(
ExtendedBigDecimal::extended_parse(&format!("0x1p{}", u64::MAX as u128 + 1)),
Err(ExtendedParserError::Overflow(ExtendedBigDecimal::Infinity))
));
assert!(matches!(
ExtendedBigDecimal::extended_parse(&format!("-0x100P{}", u64::MAX as u128 + 1)),
Err(ExtendedParserError::Overflow(
ExtendedBigDecimal::MinusInfinity
))
));
assert!(matches!(
ExtendedBigDecimal::extended_parse(&format!("0x1p-{}", u64::MAX as u128 + 1)),
Err(ExtendedParserError::Underflow(ebd)) if ebd == ExtendedBigDecimal::zero()
));
assert!(matches!(
ExtendedBigDecimal::extended_parse(&format!("-0x0.100p-{}", u64::MAX as u128 + 1)),
Err(ExtendedParserError::Underflow(
ExtendedBigDecimal::MinusZero
))
));
assert_eq!(
f64::extended_parse("0x"),
Err(ExtendedParserError::PartialMatch(0.0, "x".to_string()))
);
assert_eq!(
f64::extended_parse("0x."),
Err(ExtendedParserError::PartialMatch(0.0, "x.".to_string()))
);
assert_eq!(
f64::extended_parse("0xp"),
Err(ExtendedParserError::PartialMatch(0.0, "xp".to_string()))
);
assert_eq!(
f64::extended_parse("0xp-2"),
Err(ExtendedParserError::PartialMatch(0.0, "xp-2".to_string()))
);
assert_eq!(
f64::extended_parse("0x.p-2"),
Err(ExtendedParserError::PartialMatch(0.0, "x.p-2".to_string()))
);
assert_eq!(
f64::extended_parse("0X"),
Err(ExtendedParserError::PartialMatch(0.0, "X".to_string()))
);
assert_eq!(
f64::extended_parse("-0x"),
Err(ExtendedParserError::PartialMatch(0.0, "x".to_string()))
);
assert_eq!(
f64::extended_parse("+0x"),
Err(ExtendedParserError::PartialMatch(0.0, "x".to_string()))
);
assert_eq!(
f64::extended_parse("-0x."),
Err(ExtendedParserError::PartialMatch(-0.0, "x.".to_string()))
);
assert_eq!(
u64::extended_parse("0x"),
Err(ExtendedParserError::PartialMatch(0, "x".to_string()))
);
assert_eq!(
u64::extended_parse("-0x"),
Err(ExtendedParserError::PartialMatch(0, "x".to_string()))
);
assert_eq!(
i64::extended_parse("0x"),
Err(ExtendedParserError::PartialMatch(0, "x".to_string()))
);
assert_eq!(
i64::extended_parse("-0x"),
Err(ExtendedParserError::PartialMatch(0, "x".to_string()))
);
}
#[test]
fn test_octal() {
assert_eq!(Ok(0), u64::extended_parse("0"));
assert_eq!(Ok(0o123), u64::extended_parse("0123"));
assert_eq!(Ok(0o123), u64::extended_parse("+0123"));
assert_eq!(Ok(-0o123), i64::extended_parse("-0123"));
assert_eq!(Ok(0o123), u64::extended_parse("00123"));
assert_eq!(Ok(0), u64::extended_parse("00"));
assert_eq!(
u64::extended_parse("008"),
Err(ExtendedParserError::PartialMatch(0, "8".to_string()))
);
assert_eq!(
u64::extended_parse("08"),
Err(ExtendedParserError::PartialMatch(0, "8".to_string()))
);
assert_eq!(
u64::extended_parse("0."),
Err(ExtendedParserError::PartialMatch(0, ".".to_string()))
);
}
#[test]
fn test_binary() {
assert_eq!(Ok(0b1011), u64::extended_parse("0b1011"));
assert_eq!(Ok(0b1011), u64::extended_parse("0B1011"));
assert_eq!(Ok(0b1011), u64::extended_parse("+0b1011"));
assert_eq!(Ok(-0b1011), i64::extended_parse("-0b1011"));
assert_eq!(
u64::extended_parse("0b"),
Err(ExtendedParserError::PartialMatch(0, "b".to_string()))
);
assert_eq!(
u64::extended_parse("0b."),
Err(ExtendedParserError::PartialMatch(0, "b.".to_string()))
);
assert_eq!(
u64::extended_parse("-0b"),
Err(ExtendedParserError::PartialMatch(0, "b".to_string()))
);
assert_eq!(
i64::extended_parse("0b"),
Err(ExtendedParserError::PartialMatch(0, "b".to_string()))
);
assert_eq!(
i64::extended_parse("-0b"),
Err(ExtendedParserError::PartialMatch(0, "b".to_string()))
);
assert_eq!(
f64::extended_parse("0b100"),
Err(ExtendedParserError::PartialMatch(0f64, "b100".to_string()))
);
assert_eq!(
f64::extended_parse("0b100.1"),
Err(ExtendedParserError::PartialMatch(
0f64,
"b100.1".to_string()
))
);
assert_eq!(
ExtendedBigDecimal::extended_parse("0b100.1"),
Err(ExtendedParserError::PartialMatch(
ExtendedBigDecimal::zero(),
"b100.1".to_string()
))
);
assert_eq!(
ExtendedBigDecimal::extended_parse("0b"),
Err(ExtendedParserError::PartialMatch(
ExtendedBigDecimal::zero(),
"b".to_string()
))
);
assert_eq!(
ExtendedBigDecimal::extended_parse("0b."),
Err(ExtendedParserError::PartialMatch(
ExtendedBigDecimal::zero(),
"b.".to_string()
))
);
}
#[test]
fn test_parsing_with_leading_whitespace() {
assert_eq!(Ok(1), u64::extended_parse(" 0x1"));
assert_eq!(Ok(-2), i64::extended_parse(" -0x2"));
assert_eq!(Ok(-3), i64::extended_parse(" \t-0x3"));
assert_eq!(Ok(-4), i64::extended_parse(" \n-0x4"));
assert_eq!(Ok(-5), i64::extended_parse(" \n\t\u{000d}-0x5"));
assert_eq!(
Err(ExtendedParserError::PartialMatch(6, " ".to_string())),
u64::extended_parse("0x6 ")
);
assert_eq!(
Err(ExtendedParserError::PartialMatch(7, "\t".to_string())),
u64::extended_parse("0x7\t")
);
assert_eq!(
Err(ExtendedParserError::PartialMatch(8, "\n".to_string())),
u64::extended_parse("0x8\n")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
i64::extended_parse("\u{2029}-0x9")
);
assert_eq!(
Err(ExtendedParserError::NotNumeric),
i64::extended_parse("- 0x9")
);
}
}