unitdc 0.1.0

A dc-like arbitrary precision quantity calculator.
Documentation
use super::TokenizerError;
use num_bigint::{BigInt, BigUint};
use num_rational::BigRational;
use num_traits::pow::Pow;

use std::collections::VecDeque;

pub fn parse_bigrational(s: &str) -> Result<BigRational, TokenizerError> {
    let mut numerator = BigInt::from(0);
    let mut is_decimal = false;
    let mut decimal_places = 0;
    let mut is_negative = false;
    let mut is_exponent = false;
    let mut exponent = BigInt::from(0);
    let mut exponent_is_negative = false;

    let mut s = s.chars().filter(|c| *c != '_').collect::<VecDeque<_>>();

    if s[0] == '-' {
        is_negative = true;
        s.pop_front();
    }

    while let Some(ch) = s.pop_front() {
        match ch {
            '0'..='9' => {
                if is_exponent {
                    let digit = ch.to_digit(10).unwrap();
                    let exponent_digit = BigInt::from(digit);
                    exponent *= BigInt::from(10);
                    exponent += exponent_digit;
                } else if is_decimal {
                    let digit = ch.to_digit(10).unwrap();
                    let numerator_digit = BigInt::from(digit);
                    numerator *= BigInt::from(10);
                    numerator += numerator_digit;
                    decimal_places += 1;
                } else {
                    let digit = ch.to_digit(10).unwrap();
                    let numerator_digit = BigInt::from(digit);
                    numerator *= BigInt::from(10);
                    numerator += numerator_digit;
                }
            }
            '.' => {
                if is_decimal {
                    return Err(TokenizerError::InvalidCharacter(ch));
                }
                is_decimal = true;
            }
            'e' | 'E' => {
                if is_exponent {
                    return Err(TokenizerError::InvalidCharacter(ch));
                }
                is_exponent = true;
                if let Some(ch) = s.pop_front() {
                    if ch == '-' {
                        exponent_is_negative = true;
                    } else {
                        s.push_front(ch);
                    }
                }
            }
            _ => return Err(TokenizerError::InvalidCharacter(ch))?,
        }
    }

    if is_negative {
        numerator = -numerator;
    }
    if exponent_is_negative {
        exponent = -exponent;
    }

    let without_exponent = BigRational::new_raw(numerator, BigInt::from(1));

    let exponent = exponent - BigInt::from(decimal_places);

    let exp = if exponent >= BigInt::from(0) {
        BigRational::new_raw(
            BigInt::from(10).pow(BigUint::try_from(exponent).unwrap()),
            BigInt::from(1),
        )
    } else {
        BigRational::new_raw(
            BigInt::from(1),
            BigInt::from(10).pow(BigUint::try_from(-exponent).unwrap()),
        )
    };

    Ok((without_exponent * exp).reduced())
}