Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

SJ

Copyright (C) 2019-2025  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2019-2025".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Number parser

#![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}"),
        }
    }

}

/// # Number parser
#[derive(Debug)]
pub (super) struct NumberParser {
    x: Option<X>,
}

// x * 10 = x * (2 * 4 + 2) = (x * 2) * 4 + (x * 2) = (y << 2) + y
macro_rules! mul_10 { ($x: ident) => {{
    let y = $x << 1;
    (y << 2) + y
}}}

// x * 10 = x * (2 * 4 + 2) = (x * 2) * 4 + (x * 2) = (y << 2) + y
macro_rules! unsigned_checked_mul_10 { ($x: expr) => {{
    let x = $x;
    let result = mul_10!(x);
    (result >= x).then_some(result)
}}}

// x * 10 = x * (2 * 4 + 2) = (x * 2) * 4 + (x * 2) = (y << 2) + y
macro_rules! negative_checked_mul_10 { ($x: expr) => {{
    let x = $x;
    let result = mul_10!(x);
    (result <= x).then_some(result)
}}}

impl NumberParser {

    /// # Makes new instance
    #[inline(always)]
    pub fn new() -> Self {
        Self {
            x: None,
        }
    }

    /// # Adds new byte
    #[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,
                        // Cast into float. We'll handle this in parse(). Basically, we'll need either fract or exp to be some.
                        _ => 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,
                        // Cast into float. We'll handle this in parse(). Basically, we'll need either fract or exp to be some.
                        _ => 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(())
    }

    /// # Parses
    ///
    /// After calling this function, internal state will be reset.
    #[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));
    });
}

/// # Adds a digit
#[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,
        },
    }
}