#![cfg(feature="std")]
use {
core::fmt::{self, Display, Formatter, Write},
crate::{Number, Result},
};
#[derive(Debug)]
enum FloatExp {
None,
Some,
Exp {
x: f64,
fract: Option<u8>,
},
}
impl Display for FloatExp {
fn fmt(&self, f: &mut Formatter) -> core::result::Result<(), fmt::Error> {
match self {
FloatExp::None | FloatExp::Some => f.write_str(concat!()),
FloatExp::Exp { x, .. } => {
f.write_char('e')?;
x.fmt(f)
},
}
}
}
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))]
type Signed = i64;
#[cfg(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64"))]
type Unsigned = u64;
#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64")))]
type Signed = isize;
#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64")))]
type Unsigned = usize;
#[derive(Debug)]
enum X {
Signed(Signed),
Unsigned(Unsigned),
Float {
x: f64,
fract: Option<u8>,
exp: FloatExp,
},
}
impl Display for X {
fn fmt(&self, f: &mut Formatter) -> core::result::Result<(), fmt::Error> {
match self {
X::Signed(x) => x.fmt(f),
X::Unsigned(x) => x.fmt(f),
X::Float { x, fract: _, exp } => write!(f, "{x}{exp}"),
}
}
}
#[derive(Debug)]
pub (super) struct NumberParser {
x: Option<X>,
}
macro_rules! mul_10 { ($x: ident) => {{
let y = $x << 1;
(y << 2) + y
}}}
macro_rules! unsigned_checked_mul_10 { ($x: expr) => {{
let x = $x;
let result = mul_10!(x);
(result >= x).then_some(result)
}}}
macro_rules! negative_checked_mul_10 { ($x: expr) => {{
let x = $x;
let result = mul_10!(x);
(result <= x).then_some(result)
}}}
impl NumberParser {
#[inline(always)]
pub fn new() -> Self {
Self {
x: None,
}
}
#[inline(always)]
pub fn add(&mut self, b: &u8) -> Result<()> {
match b {
b'-' => match self.x.as_mut() {
None => self.x = Some(X::Signed(0)),
Some(X::Signed(x)) => return Err(err!("Invalid integer: {x}{c}", c=char::from(*b))),
Some(X::Unsigned(x)) => return Err(err!("Invalid integer: {x}{c}", c=char::from(*b))),
Some(X::Float { x, fract: _, exp }) => match exp {
FloatExp::Some => *exp = FloatExp::Exp { x: -0.0, fract: None },
_ => return Err(err!("Invalid float: {x}{c}", c=char::from(*b))),
},
},
b'+' => match self.x.as_mut() {
None => return Err(err!("Invalid sign: {c}", c=char::from(*b))),
Some(X::Signed(x)) => return Err(err!("Invalid integer: {x}{c}", c=char::from(*b))),
Some(X::Unsigned(x)) => return Err(err!("Invalid integer: {x}{c}", c=char::from(*b))),
Some(X::Float { x, fract: _, exp }) => match exp {
FloatExp::Some => *exp = FloatExp::Exp { x: 0.0, fract: None },
FloatExp::None => return Err(err!("Invalid float: {x}{c}", c=char::from(*b))),
FloatExp::Exp {..} => return Err(err!("Invalid float: {x}{exp}{c}", c=char::from(*b))),
},
},
b'e' | b'E' => match self.x.as_mut() {
None => return Err(err!("Invalid sign: {c}", c=char::from(*b))),
Some(X::Signed(x)) => self.x = Some(X::Float { x: *x as f64, fract: None, exp: FloatExp::Some }),
Some(X::Unsigned(x)) => self.x = Some(X::Float { x: *x as f64, fract: None, exp: FloatExp::Some }),
Some(X::Float { x, fract: _, exp }) => match exp {
FloatExp::None => *exp = FloatExp::Some,
FloatExp::Some => return Err(err!("Invalid float: {x}e{c}", c=char::from(*b))),
FloatExp::Exp {..} => return Err(err!("Invalid float: {x}{exp}{c}", c=char::from(*b))),
},
},
b'.' => match self.x.as_mut() {
None => return Err(err!("Invalid sign: {c}", c=char::from(*b))),
Some(X::Signed(x)) => self.x = Some(X::Float { x: *x as f64, fract: Some(0), exp: FloatExp::None }),
Some(X::Unsigned(x)) => self.x = Some(X::Float { x: *x as f64, fract: Some(0), exp: FloatExp::None }),
Some(X::Float { x, fract, exp }) => match fract {
None => *fract = Some(0),
Some(_) => match exp {
FloatExp::None => return Err(err!("Invalid float: {x}{c}", c=char::from(*b))),
FloatExp::Some => return Err(err!("Invalid float: {x}e{c}", c=char::from(*b))),
FloatExp::Exp { x: _, fract } => match fract {
None => *fract = Some(0),
Some(_) => return Err(err!("Invalid float: {x}{exp}{c}", c=char::from(*b))),
},
},
},
},
b'0'..=b'9' => {
let b = b - b'0';
match &mut self.x {
None => self.x = Some(X::Unsigned(b.into())),
Some(X::Signed(x)) => match negative_checked_mul_10!(*x).map(|x| x.checked_sub(b.into())) {
Some(Some(n)) => *x = n,
_ => self.x = Some(X::Float { x: *x as f64 * 10.0 - f64::from(b), fract: None, exp: FloatExp::None }),
},
Some(X::Unsigned(x)) => match unsigned_checked_mul_10!(*x).map(|x| x.checked_add(b.into())) {
Some(Some(n)) => *x = n,
_ => self.x = Some(X::Float { x: *x as f64 * 10.0 + f64::from(b), fract: None, exp: FloatExp::None }),
},
Some(X::Float { x, fract, exp }) => match exp {
FloatExp::None => if add_digit(x, fract.as_mut(), b) == false {
return Err(err!("Float fractional part is too long: {x}{b}"));
},
FloatExp::Some => *exp = FloatExp::Exp { x: b.into(), fract: None },
FloatExp::Exp { x, fract } => if add_digit(x, fract.as_mut(), b) == false {
return Err(err!("Float's exponent's fractional part is too long: {x}{b}"));
},
},
};
},
_ => return Err(match &self.x {
None => err!("Invalid number: {c}", c=char::from(*b)),
Some(X::Signed(x)) => err!("Invalid number: {x}{c}", c=char::from(*b)),
Some(X::Unsigned(x)) => err!("Invalid number: {x}{c}", c=char::from(*b)),
Some(x) => err!("Invalid number: {x}{c}", c=char::from(*b)),
}),
};
Ok(())
}
#[inline]
pub fn parse(&mut self) -> Result<Number> {
match self.x.take() {
None => Err(e!("Number is empty")),
Some(X::Signed(x)) => Ok(x.into()),
Some(X::Unsigned(x)) => Ok(x.into()),
Some(X::Float { x, fract, exp }) => match exp {
FloatExp::None => if fract.is_some() {
Ok(x.into())
} else {
let large_or_small = if x.is_sign_positive() { "large" } else { "small" };
Err(err!("Integer too {large_or_small}: {x}", x=x.trunc()))
},
FloatExp::Some => Err(err!("Invalid float: {x}e")),
FloatExp::Exp { x: exp_x, fract: _ } => Ok((x * 10_f64.powf(exp_x)).into()),
},
}
}
}
const FRACTS: [[f64; 10]; 20] = [
[0.0, 1e-1, 2e-1, 3e-1, 4e-1, 5e-1, 6e-1, 7e-1, 8e-1, 9e-1],
[0.0, 1e-2, 2e-2, 3e-2, 4e-2, 5e-2, 6e-2, 7e-2, 8e-2, 9e-2],
[0.0, 1e-3, 2e-3, 3e-3, 4e-3, 5e-3, 6e-3, 7e-3, 8e-3, 9e-3],
[0.0, 1e-4, 2e-4, 3e-4, 4e-4, 5e-4, 6e-4, 7e-4, 8e-4, 9e-4],
[0.0, 1e-5, 2e-5, 3e-5, 4e-5, 5e-5, 6e-5, 7e-5, 8e-5, 9e-5],
[0.0, 1e-6, 2e-6, 3e-6, 4e-6, 5e-6, 6e-6, 7e-6, 8e-6, 9e-6],
[0.0, 1e-7, 2e-7, 3e-7, 4e-7, 5e-7, 6e-7, 7e-7, 8e-7, 9e-7],
[0.0, 1e-8, 2e-8, 3e-8, 4e-8, 5e-8, 6e-8, 7e-8, 8e-8, 9e-8],
[0.0, 1e-9, 2e-9, 3e-9, 4e-9, 5e-9, 6e-9, 7e-9, 8e-9, 9e-9],
[0.0, 1e-10, 2e-10, 3e-10, 4e-10, 5e-10, 6e-10, 7e-10, 8e-10, 9e-10],
[0.0, 1e-11, 2e-11, 3e-11, 4e-11, 5e-11, 6e-11, 7e-11, 8e-11, 9e-11],
[0.0, 1e-12, 2e-12, 3e-12, 4e-12, 5e-12, 6e-12, 7e-12, 8e-12, 9e-12],
[0.0, 1e-13, 2e-13, 3e-13, 4e-13, 5e-13, 6e-13, 7e-13, 8e-13, 9e-13],
[0.0, 1e-14, 2e-14, 3e-14, 4e-14, 5e-14, 6e-14, 7e-14, 8e-14, 9e-14],
[0.0, 1e-15, 2e-15, 3e-15, 4e-15, 5e-15, 6e-15, 7e-15, 8e-15, 9e-15],
[0.0, 1e-16, 2e-16, 3e-16, 4e-16, 5e-16, 6e-16, 7e-16, 8e-16, 9e-16],
[0.0, 1e-17, 2e-17, 3e-17, 4e-17, 5e-17, 6e-17, 7e-17, 8e-17, 9e-17],
[0.0, 1e-18, 2e-18, 3e-18, 4e-18, 5e-18, 6e-18, 7e-18, 8e-18, 9e-18],
[0.0, 1e-19, 2e-19, 3e-19, 4e-19, 5e-19, 6e-19, 7e-19, 8e-19, 9e-19],
[0.0, 1e-20, 2e-20, 3e-20, 4e-20, 5e-20, 6e-20, 7e-20, 8e-20, 9e-20],
];
#[test]
fn tests() {
FRACTS.iter().enumerate().for_each(|(i, fracts)| {
assert_eq!(fracts.len(), 10);
assert_eq!(fracts[0], 0.0);
fracts[1..].iter().enumerate().for_each(|(k, f)| assert!((f - 10_f64.powf(-((i + 1) as f64)) * (k + 1) as f64).abs() < 1e-10));
});
}
#[must_use]
#[inline]
fn add_digit(x: &mut f64, fract: Option<&mut u8>, digit: u8) -> bool {
if digit > 9 {
return false;
}
match fract {
None => {
*x = *x * 10.0;
if digit > 0 {
let digit = f64::from(digit);
if x.is_sign_positive() {
*x += digit;
} else {
*x -= digit;
}
}
true
},
Some(fract) => match fract.checked_add(1) {
Some(new_fract) => {
if digit > 0 {
let digit = match FRACTS.get(usize::from(*fract)).map(|fracts| fracts.get(usize::from(digit))) {
Some(Some(digit)) => *digit,
_ => 10_f64.powf(-f64::from(new_fract)) * f64::from(digit),
};
if x.is_sign_positive() {
*x += digit;
} else {
*x -= digit;
}
}
*fract = new_fract;
true
},
None => false,
},
}
}