fastnum2 0.3.6

fork of Fast decimal numbers library
Documentation
macro_rules! test_impl {
    (D, $bits: literal) => {
        paste::paste! { test_impl!(SIGNED: $bits, [< dec $bits >], [< u $bits >], [<D $bits>], [<U $bits>]); }
    };
    (UD, $bits: literal) => {
        paste::paste! { test_impl!(UNSIGNED: $bits, [< udec $bits >], [< u $bits >], [<UD $bits>], [<U $bits>]); }
    };
    (UNSIGNED: $bits: tt, $dec: ident, $uint: ident, $D: ident, $U: ident) => {
        mod $dec {
            use rstest::*;
            use fastnum::{*, decimal::*};

            super::test_impl!(COMMON:: $bits, $uint, $D, $U, THIS);
            super::test_impl!(UNSIGNED:: $bits, $uint, $D, $U, THIS);
        }
    };
    (SIGNED: $bits: tt, $dec: ident, $uint: ident, $D: ident, $U: ident) => {
        mod $dec {
            use rstest::*;
            use fastnum::{*, decimal::*};

            super::test_impl!(COMMON:: $bits, $uint, $D, $U, THIS);
            super::test_impl!(SIGNED:: $bits, $uint, $D, $U, THIS);
        }
    };

    (COMMON:: 512, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(COMMON:: 256, $uint, $D, $U);

        #[rstest(::trace)]
        #[case("115792089237316195423570985008687907853269984665640564039457584007913129639935", $uint!(115792089237316195423570985008687907853269984665640564039457584007913129639935), 0)]
        #[case("1157920892373161954235709850086.87907853269984665640564039457584007913129639935", $uint!(115792089237316195423570985008687907853269984665640564039457584007913129639935), -47)]
        #[case("13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095", $uint!(13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095), 0)]
        #[case("1340780792994259709957402499820584612747936582.0592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095", $uint!(13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095), -109)]
        fn test_parse_ok_512(#[case] s: &str, #[case] _int: $U, #[case] exp: i16) {
            let dec = $D::from_str(s, Context::default()).unwrap();
            assert_eq!(dec.digits(), _int);
            assert_eq!(dec.fractional_digits_count(), -exp);
            assert!(dec.is_op_ok());
        }

        #[rstest(::trace)]
        #[case("13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096")]
        #[case("13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095.1")]
        #[should_panic(expected = "(fastnum) number too large to fit in target type")]
        fn test_parse_overflow_512(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }
    };
    (UNSIGNED:: 512, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(UNSIGNED:: 256, $uint, $D, $U);
    };
    (SIGNED:: 512, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(SIGNED:: 256, $uint, $D, $U);
    };

    (COMMON:: 256, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(COMMON:: 256, $uint, $D, $U);

        #[rstest(::trace)]
        #[case("115792089237316195423570985008687907853269984665640564039457584007913129639936")]
        #[case("11579208923731619542357098500868790785326998466564056403945758400791312963993.6")]
        #[case("1157920892373161954235709850086879078532699846656405640394575840079131296399351")]
        #[case("115792089237316195423570985008687907853269984665640564039457584007913129639935.1")]
        #[should_panic(expected = "(fastnum) number too large to fit in target type")]
        fn test_parse_overflow_256(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }

    };
    (COMMON:: 256, $uint: ident, $D: ident, $U: ident) => {
        super::test_impl!(COMMON:: 128, $uint, $D, $U);

        #[rstest(::trace)]
        #[case("340282366920938463463374607431768211455", $uint!(340282366920938463463374607431768211455), 0)]
        #[case("34028236692093846346337460743176821145.5", $uint!(340282366920938463463374607431768211455), -1)]
        #[case("34028236692093846346337460743176821145.5e1000", $uint!(340282366920938463463374607431768211455), 999)]
        #[case("340282366920938463.463374607431768211455e-1000", $uint!(340282366920938463463374607431768211455), -1021)]
        #[case("115792089237316195423570985008687907853269984665640564039457584007913129639935", $uint!(115792089237316195423570985008687907853269984665640564039457584007913129639935), 0)]
        #[case("1157920892373161954235709850086.87907853269984665640564039457584007913129639935", $uint!(115792089237316195423570985008687907853269984665640564039457584007913129639935), -47)]
        fn test_parse_ok_256(#[case] s: &str, #[case] _int: $U, #[case] exp: i16) {
            let dec = $D::from_str(s, Context::default()).unwrap();
            assert_eq!(dec.digits(), _int);
            assert_eq!(dec.fractional_digits_count(), -exp);
            assert!(dec.is_op_ok());
        }
    };
    (UNSIGNED:: 256, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(UNSIGNED:: 256, $uint, $D, $U);
    };
    (UNSIGNED:: 256, $uint: ident, $D: ident, $U: ident) => {
        super::test_impl!(UNSIGNED:: 128, $uint, $D, $U);
    };
    (SIGNED:: 256, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(SIGNED:: 256, $uint, $D, $U);
    };
    (SIGNED:: 256, $uint: ident, $D: ident, $U: ident) => {
        super::test_impl!(SIGNED:: 128, $uint, $D, $U);
    };

    (COMMON:: 128, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(COMMON:: 128, $uint, $D, $U);

        #[rstest(::trace)]
        #[case("340282366920938463463374607431768211456")]
        #[case("340282366920938463463374607431768211455.5")]
        #[case("340282366920938463.4633746074317682114551")]
        #[case("1157920892373161954235709850086879078532699846656405640394575840079131296399351")]
        #[case("115792089237316195423570985008687907853269984665640564039457584007913129639935.1")]
        #[should_panic(expected = "(fastnum) number too large to fit in target type")]
        fn test_parse_overflow_128(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }

    };
    (COMMON:: 128, $uint: ident, $D: ident, $U: ident) => {
        #[rstest(::trace)]
        #[case("0", $uint!(0), 0)]
        #[case("1", $uint!(1), 0)]
        #[case("01", $uint!(1), 0)]
        #[case("001", $uint!(1), 0)]
        #[case("0010", $uint!(10), 0)]
        #[case("+0", $uint!(0), 0)]
        #[case("+1", $uint!(1), 0)]
        #[case("+01", $uint!(1), 0)]
        #[case("1331.107", $uint!(1331107), -3)]
        #[case(".107",  $uint!(107), -3)]
        #[case("0.0000012345", $uint!(12345), -10)]
        #[case("5e9", $uint!(5), 9)]
        #[case("10000000000000000000", $uint!(10000000000000000000), 0)]
        #[case("10000000000000000001", $uint!(10000000000000000001), 0)]
        #[case("100000000000000000000", $uint!(100000000000000000000), 0)]
        #[case("0.000000000000000000000000000000000000001", $uint!(1), -39)]
        #[case("18446744073709551615", $uint!(18446744073709551615), 0)]
        #[case("18446744073709551616", $uint!(18446744073709551616), 0)]
        #[case("18_446_744_073_709_551_615", $uint!(18446744073709551615), 0)]
        #[case("20935706972060549068014", $uint!(20935706972060549068014), 0)]
        #[case("1.0", $uint!(10), -1)]
        #[case("2e1", $uint!(2), 1)]
        #[case("2e0", $uint!(2), 0)]
        #[case("0.00123", $uint!(123), -5)]
        #[case("123", $uint!(123), 0)]
        #[case("1230", $uint!(1230), 0)]
        #[case("12.3", $uint!(123), -1)]
        #[case("123e-1", $uint!(123), -1)]
        #[case("1.23e+1", $uint!(123), -1)]
        #[case("1.23E+3", $uint!(123), 1)]
        #[case("1.23E-8", $uint!(123), -10)]
        #[case("1.23E-10", $uint!(123), -12)]
        #[case("123_", $uint!(123), 0)]
        #[case("31_862_140.830_686_979", $uint!(31862140830686979), -9)]
        #[case("1_1.2_2", $uint!(1122), -2)]
        #[case("999.521_939", $uint!(999521939), -6)]
        #[case("679.35_84_03E-2", $uint!(679358403), -8)]
        #[case("271576662.__E4", $uint!(271576662), 4)]
        #[case("1_._2", $uint!(12), -1)]
        #[case("25.8", $uint!(258), -1)]
        #[case("0.000000034283", $uint!(34283), -12)]
        #[case("20935.706972060549068014", $uint!(20935706972060549068014), -18)]
        #[case("0.20935706972060549068014", $uint!(20935706972060549068014), -23)]
        #[case("340282366920938463463374607431768211455", $uint!(340282366920938463463374607431768211455), 0)]
        #[case("34028236692093846346337460743176821145.5", $uint!(340282366920938463463374607431768211455), -1)]
        #[case("34028236692093846346337460743176821145.5e1000", $uint!(340282366920938463463374607431768211455), 999)]
        #[case("340282366920938463.463374607431768211455e-1000", $uint!(340282366920938463463374607431768211455), -1021)]
        fn test_parse_ok(#[case] s: &str, #[case] _int: $U, #[case] exp: i16) {
            let dec = $D::from_str(s, Context::default()).unwrap();
            assert_eq!(dec.digits(), _int);
            assert_eq!(dec.fractional_digits_count(), -exp);
            assert!(dec.is_op_ok());
        }

        #[rstest(::trace)]
        #[case::nan("nan")]
        #[case::nan("NAN")]
        #[case::nan("NaN")]
        fn test_parse_nan(#[case] s: &str) {
            let dec = $D::from_str(s, Context::default()).unwrap();
            assert!(dec.is_nan());
        }

        #[rstest(::trace)]
        #[case::nan("Inf")]
        #[case::nan("+Inf")]
        #[case::nan("+Infinity")]
        fn test_parse_inf(#[case] s: &str) {
            let dec = $D::from_str(s, Context::default()).unwrap();
            assert!(dec.is_infinite());
            assert_eq!(dec, $D::INFINITY);
        }

        #[rstest(::trace)]
        #[case::empty("")]
        #[case::only_minus("-")]
        #[case::only_plus("+")]
        #[case::only_decimal_and_underscore("_._")]
        #[case::empty_exponent("123.123E")]
        #[case::only_decimal_point(".")]
        #[case::only_decimal_and_exponent(".e4")]
        #[case::only_exponent("e4")]
        #[should_panic(expected = "(fastnum) cannot parse decimal from empty string")]
        fn test_parse_empty(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }

        #[rstest(::trace)]
        #[case::hello("hello")]
        #[case::incorrect_nan("nan1")]
        #[case::incorrect_inf("Inf1")]
        #[case::neg_nan("-nan")]
        #[case::incorrect_inf("-Inf1")]
        #[case::incorrect_inf("-InfinityInf")]
        #[case::several_dots("123.45.67")]
        #[case::invalid_char("12z3.12")]
        #[case::invalid_char("12💖3.12")]
        #[case::invalid_char("💖")]
        #[case::nan_exponent("123.123eg")]
        #[case::multiple_decimal_points("123.12.45")]
        #[case::string_hex("0xCafeBeef")]
        #[case::several_exponent("123.34e-1e-2")]
        #[case::invalid_exponent("123.34e-1.5")]
        #[should_panic(expected = "(fastnum) invalid literal found in string")]
        fn test_parse_invalid_digit(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }

        #[rstest(::trace)]
        #[case("1e-9223372036854775808")]
        #[case("1e9223372036854775809")]
        #[should_panic(expected = "(fastnum) exponent is too large to fit in target type")]
        fn test_parse_exponent_overflow(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }
    };
    (UNSIGNED:: 128, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(UNSIGNED:: 128, $uint, $D, $U);
    };
    (UNSIGNED:: 128, $uint: ident, $D: ident, $U: ident) => {
        #[rstest(::trace)]
        #[case::minus_sign("-0")]
        #[case::minus_sign("-0.0")]
        #[case::minus_sign("-1")]
        #[case::minus_sign("-1.434343")]
        #[should_panic(expected = "(fastnum) number would be signed for unsigned type")]
        fn test_parse_unsigned(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }
    };
    (SIGNED:: 128, $uint: ident, $D: ident, $U: ident, THIS) => {
        super::test_impl!(SIGNED:: 128, $uint, $D, $U);

        #[rstest(::trace)]
        #[case("-340282366920938463463374607431768211456")]
        #[case("-340282366920938463463374607431768211455.5")]
        #[case("-340282366920938463.4633746074317682114551")]
        #[case("-1157920892373161954235709850086879078532699846656405640394575840079131296399351")]
        #[case("-115792089237316195423570985008687907853269984665640564039457584007913129639935.1")]
        #[should_panic(expected = "(fastnum) number too small to fit in target type")]
        fn test_parse_overflow_128_signed(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }
    };
    (SIGNED:: 128, $uint: ident, $D: ident, $U: ident) => {
        #[rstest(::trace)]
        #[case("-0", Sign::Minus, $uint!(0), 0)]
        #[case("-1", Sign::Minus, $uint!(1), 0)]
        #[case("-.107", Sign::Minus, $uint!(107), -3)]
        #[case("-0.000000000000000000000000000000000000001", Sign::Minus, $uint!(1), -39)]
        #[case("-123", Sign::Minus, $uint!(123), 0)]
        #[case("-1230", Sign::Minus, $uint!(1230), 0)]
        #[case("-1.23E-10", Sign::Minus, $uint!(123), -12)]
        #[case("-1_1.2_2", Sign::Minus, $uint!(1122), -2)]
        #[case("-34028236692093846346337460743176821145.5", Sign::Minus, $uint!(340282366920938463463374607431768211455), -1)]
        #[case("-340282366920938463.463374607431768211455e-1000", Sign::Minus, $uint!(340282366920938463463374607431768211455), -1021)]
        fn test_parse_ok_signed(
            #[case] s: &str,
            #[case] sign: Sign,
            #[case] _int: $U,
            #[case] exp: i16,
        ) {
            let dec = $D::from_str(s, Context::default()).unwrap();
            assert_eq!(dec.digits(), _int);
            assert_eq!(dec.sign(), sign);
            assert_eq!(dec.fractional_digits_count(), -exp);
            assert!(dec.is_op_ok());
        }

        #[rstest(::trace)]
        #[case::nan("-Infinity")]
        #[case::nan("-Inf")]
        fn test_parse_inf_neg(#[case] s: &str) {
            let dec = $D::from_str(s, Context::default()).unwrap();
            assert!(dec.is_infinite());
            assert_eq!(dec, $D::NEG_INFINITY);
        }

        #[rstest(::trace)]
        #[case("-0-1")]
        #[case("-1+")]
        #[case("+0+1")]
        #[case("--1")]
        #[case("+-35.55")]
        #[should_panic(expected = "(fastnum) invalid literal found in string")]
        fn test_parse_invalid_signed(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }

        #[rstest(::trace)]
        #[case::invalid_exponent("-1e9223372036854775809")]
        #[case::invalid_exponent("-1e-9223372036854775808")]
        #[should_panic(expected = "(fastnum) exponent is too large to fit in target type")]
        fn test_parse_exponent_overflow_signed(#[case] s: &str) {
            let _ = $D::from_str(s, Context::default()).unwrap();
        }
    };
}

pub(crate) use test_impl;