diff options
Diffstat (limited to 'third_party/rust/minimal-lexical/tests')
-rw-r--r-- | third_party/rust/minimal-lexical/tests/bellerophon.rs | 59 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/bellerophon_tests.rs | 231 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/integration_tests.rs | 228 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/lemire_tests.rs | 378 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/libm_tests.rs | 289 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/mask_tests.rs | 16 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/number_tests.rs | 88 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/parse_tests.rs | 189 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/rounding_tests.rs | 64 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/slow_tests.rs | 337 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/stackvec.rs | 32 | ||||
-rw-r--r-- | third_party/rust/minimal-lexical/tests/vec_tests.rs | 395 |
12 files changed, 2306 insertions, 0 deletions
diff --git a/third_party/rust/minimal-lexical/tests/bellerophon.rs b/third_party/rust/minimal-lexical/tests/bellerophon.rs new file mode 100644 index 0000000000..99cd89acfc --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/bellerophon.rs @@ -0,0 +1,59 @@ +#![cfg(feature = "compact")] +#![allow(dead_code)] + +use minimal_lexical::bellerophon::bellerophon; +use minimal_lexical::extended_float::{extended_to_float, ExtendedFloat}; +use minimal_lexical::num::Float; +use minimal_lexical::number::Number; + +pub fn bellerophon_test<F: Float + core::fmt::Debug>( + xmant: u64, + xexp: i32, + many_digits: bool, + ymant: u64, + yexp: i32, +) { + let num = Number { + exponent: xexp, + mantissa: xmant, + many_digits, + }; + let xfp = bellerophon::<F>(&num); + let yfp = ExtendedFloat { + mant: ymant, + exp: yexp, + }; + // Given us useful error messages if the floats are valid. + if xfp.exp >= 0 && yfp.exp >= 0 { + assert!( + xfp == yfp, + "x != y, xfp={:?}, yfp={:?}, x={:?}, y={:?}", + xfp, + yfp, + extended_to_float::<F>(xfp), + extended_to_float::<F>(yfp) + ); + } else { + assert_eq!(xfp, yfp); + } +} + +pub fn compute_float32(q: i32, w: u64) -> (i32, u64) { + let num = Number { + exponent: q, + mantissa: w, + many_digits: false, + }; + let fp = bellerophon::<f32>(&num); + (fp.exp, fp.mant) +} + +pub fn compute_float64(q: i32, w: u64) -> (i32, u64) { + let num = Number { + exponent: q, + mantissa: w, + many_digits: false, + }; + let fp = bellerophon::<f64>(&num); + (fp.exp, fp.mant) +} diff --git a/third_party/rust/minimal-lexical/tests/bellerophon_tests.rs b/third_party/rust/minimal-lexical/tests/bellerophon_tests.rs new file mode 100644 index 0000000000..f5826c615d --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/bellerophon_tests.rs @@ -0,0 +1,231 @@ +#![cfg(feature = "compact")] + +mod bellerophon; + +use bellerophon::{bellerophon_test, compute_float32, compute_float64}; +use minimal_lexical::num::Float; + +#[test] +fn halfway_round_down_test() { + // Halfway, round-down tests + bellerophon_test::<f64>(9007199254740992, 0, false, 0, 1076); + bellerophon_test::<f64>( + 9007199254740993, + 0, + false, + 9223372036854776832, + 1065 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(9007199254740994, 0, false, 1, 1076); + + bellerophon_test::<f64>(18014398509481984, 0, false, 0, 1077); + bellerophon_test::<f64>( + 18014398509481986, + 0, + false, + 9223372036854776832, + 1066 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(18014398509481988, 0, false, 1, 1077); + + bellerophon_test::<f64>(9223372036854775808, 0, false, 0, 1086); + bellerophon_test::<f64>( + 9223372036854776832, + 0, + false, + 9223372036854776832, + 1075 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(9223372036854777856, 0, false, 1, 1086); + + // Add a 0 but say we're truncated. + bellerophon_test::<f64>(9007199254740992000, -3, true, 0, 1076); + bellerophon_test::<f64>( + 9007199254740993000, + -3, + true, + 9223372036854776832, + 1065 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(9007199254740994000, -3, true, 1, 1076); +} + +#[test] +fn halfway_round_up_test() { + // Halfway, round-up tests + bellerophon_test::<f64>(9007199254740994, 0, false, 1, 1076); + bellerophon_test::<f64>( + 9007199254740995, + 0, + false, + 9223372036854778880, + 1065 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(9007199254740996, 0, false, 2, 1076); + + bellerophon_test::<f64>(18014398509481988, 0, false, 1, 1077); + bellerophon_test::<f64>( + 18014398509481990, + 0, + false, + 9223372036854778880, + 1066 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(18014398509481992, 0, false, 2, 1077); + + bellerophon_test::<f64>(9223372036854777856, 0, false, 1, 1086); + bellerophon_test::<f64>( + 9223372036854778880, + 0, + false, + 9223372036854778880, + 1075 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(9223372036854779904, 0, false, 2, 1086); + + // Add a 0 but say we're truncated. + bellerophon_test::<f64>(9007199254740994000, -3, true, 1, 1076); + bellerophon_test::<f64>( + 9007199254740994990, + -3, + true, + 9223372036854778869, + 1065 + f64::INVALID_FP, + ); + bellerophon_test::<f64>( + 9007199254740995000, + -3, + true, + 9223372036854778879, + 1065 + f64::INVALID_FP, + ); + bellerophon_test::<f64>( + 9007199254740995010, + -3, + true, + 9223372036854778890, + 1065 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(9007199254740995050, -3, true, 2, 1076); + bellerophon_test::<f64>(9007199254740996000, -3, true, 2, 1076); +} + +#[test] +fn extremes_test() { + // Need to check we get proper results with rounding for near-infinity + // and near-zero and/or denormal floats. + bellerophon_test::<f64>(5, -324, false, 1, 0); + bellerophon_test::<f64>(10, -324, false, 2, 0); + // This is very close to 2.4703282292062327206e-342. + bellerophon_test::<f64>( + 2470328229206232720, + -342, + false, + 18446744073709551608, + -64 + f64::INVALID_FP, + ); + bellerophon_test::<f64>(2470328229206232721, -342, false, 9223372036854775808, -32831); + bellerophon_test::<f64>(2470328229206232725, -342, false, 9223372036854775824, -32831); + bellerophon_test::<f64>(2470328229206232726, -342, false, 1, 0); + bellerophon_test::<f64>(2470328229206232730, -342, false, 1, 0); + // Check very close to literal infinity. + // 17.976931348623155 + // 1.797693134862315508561243283845062402343434371574593359244049e+308 + // 1.797693134862315708145274237317043567980705675258449965989175e+308 + bellerophon_test::<f64>(17976931348623155, 292, false, 4503599627370494, 2046); + bellerophon_test::<f64>(17976931348623156, 292, false, 4503599627370494, 2046); + bellerophon_test::<f64>(1797693134862315605, 290, false, 4503599627370494, 2046); + bellerophon_test::<f64>(1797693134862315607, 290, false, 4503599627370494, 2046); + bellerophon_test::<f64>(1797693134862315608, 290, false, 18446744073709548540, -30733); + bellerophon_test::<f64>(1797693134862315609, 290, false, 18446744073709548550, -30733); + bellerophon_test::<f64>(179769313486231561, 291, false, 4503599627370495, 2046); + bellerophon_test::<f64>(17976931348623157, 292, false, 4503599627370495, 2046); + + // Check existing issues and underflow. + bellerophon_test::<f64>(2470328229206232726, -343, false, 0, 0); + bellerophon_test::<f64>(2470328229206232726, -342, false, 1, 0); + bellerophon_test::<f64>(1, -250, false, 1945308223406668, 192); + bellerophon_test::<f64>(1, -150, false, 2867420733609077, 524); + bellerophon_test::<f64>(1, -45, false, 1924152549665465, 873); + bellerophon_test::<f64>(1, -40, false, 400386103400348, 890); + bellerophon_test::<f64>(1, -20, false, 2142540351554083, 956); + bellerophon_test::<f64>(1, 0, false, 0, 1023); + bellerophon_test::<f64>(1, 20, false, 1599915997629504, 1089); + bellerophon_test::<f64>(1, 40, false, 3768206498159781, 1155); + bellerophon_test::<f64>(1, 150, false, 999684479948463, 1521); + bellerophon_test::<f64>(1, 250, false, 1786584717939204, 1853); + // Minimum positive normal float. + bellerophon_test::<f64>(22250738585072014, -324, false, 0, 1); + // Maximum positive subnormal float. + bellerophon_test::<f64>(2225073858507201, -323, false, 4503599627370495, 0); + // Next highest subnormal float. + bellerophon_test::<f64>(22250738585072004, -324, false, 4503599627370494, 0); + bellerophon_test::<f64>(22250738585072006, -324, false, 4503599627370494, 0); + bellerophon_test::<f64>(22250738585072007, -324, false, 4503599627370495, 0); + bellerophon_test::<f64>(222507385850720062, -325, false, 4503599627370494, 0); + bellerophon_test::<f64>(222507385850720063, -325, false, 4503599627370494, 0); + bellerophon_test::<f64>(222507385850720064, -325, false, 4503599627370494, 0); + bellerophon_test::<f64>(2225073858507200641, -326, false, 18446744073709545462, -32779); + bellerophon_test::<f64>(2225073858507200642, -326, false, 18446744073709545472, -32779); + bellerophon_test::<f64>(222507385850720065, -325, false, 4503599627370495, 0); +} + +#[test] +fn compute_float_f32_test() { + // These test near-halfway cases for single-precision floats. + assert_eq!(compute_float32(0, 16777216), (151, 0)); + assert_eq!(compute_float32(0, 16777217), (111 + f32::INVALID_FP, 9223372586610589696)); + assert_eq!(compute_float32(0, 16777218), (151, 1)); + assert_eq!(compute_float32(0, 16777219), (111 + f32::INVALID_FP, 9223373686122217472)); + assert_eq!(compute_float32(0, 16777220), (151, 2)); + + // These are examples of the above tests, with + // digits from the exponent shifted to the mantissa. + assert_eq!(compute_float32(-10, 167772160000000000), (151, 0)); + assert_eq!( + compute_float32(-10, 167772170000000000), + (111 + f32::INVALID_FP, 9223372586610589696) + ); + assert_eq!(compute_float32(-10, 167772180000000000), (151, 1)); + // Let's check the lines to see if anything is different in table... + assert_eq!( + compute_float32(-10, 167772190000000000), + (111 + f32::INVALID_FP, 9223373686122217472) + ); + assert_eq!(compute_float32(-10, 167772200000000000), (151, 2)); +} + +#[test] +fn compute_float_f64_test() { + // These test near-halfway cases for double-precision floats. + assert_eq!(compute_float64(0, 9007199254740992), (1076, 0)); + assert_eq!(compute_float64(0, 9007199254740993), (1065 + f64::INVALID_FP, 9223372036854776832)); + assert_eq!(compute_float64(0, 9007199254740994), (1076, 1)); + assert_eq!(compute_float64(0, 9007199254740995), (1065 + f64::INVALID_FP, 9223372036854778880)); + assert_eq!(compute_float64(0, 9007199254740996), (1076, 2)); + assert_eq!(compute_float64(0, 18014398509481984), (1077, 0)); + assert_eq!( + compute_float64(0, 18014398509481986), + (1066 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!(compute_float64(0, 18014398509481988), (1077, 1)); + assert_eq!( + compute_float64(0, 18014398509481990), + (1066 + f64::INVALID_FP, 9223372036854778880) + ); + assert_eq!(compute_float64(0, 18014398509481992), (1077, 2)); + + // These are examples of the above tests, with + // digits from the exponent shifted to the mantissa. + assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0)); + assert_eq!( + compute_float64(-3, 9007199254740993000), + (1065 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1)); + assert_eq!( + compute_float64(-3, 9007199254740995000), + (1065 + f64::INVALID_FP, 9223372036854778879) + ); + assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2)); +} diff --git a/third_party/rust/minimal-lexical/tests/integration_tests.rs b/third_party/rust/minimal-lexical/tests/integration_tests.rs new file mode 100644 index 0000000000..a8f2ff8a0e --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/integration_tests.rs @@ -0,0 +1,228 @@ +/// Find and parse sign and get remaining bytes. +#[inline] +fn parse_sign<'a>(bytes: &'a [u8]) -> (bool, &'a [u8]) { + match bytes.get(0) { + Some(&b'+') => (true, &bytes[1..]), + Some(&b'-') => (false, &bytes[1..]), + _ => (true, bytes), + } +} + +// Convert u8 to digit. +#[inline] +fn to_digit(c: u8) -> Option<u32> { + (c as char).to_digit(10) +} + +// Add digit from exponent. +#[inline] +fn add_digit_i32(value: i32, digit: u32) -> Option<i32> { + return value.checked_mul(10)?.checked_add(digit as i32); +} + +// Subtract digit from exponent. +#[inline] +fn sub_digit_i32(value: i32, digit: u32) -> Option<i32> { + return value.checked_mul(10)?.checked_sub(digit as i32); +} + +// Convert character to digit. +#[inline] +fn is_digit(c: u8) -> bool { + to_digit(c).is_some() +} + +// Split buffer at index. +#[inline] +fn split_at_index<'a>(digits: &'a [u8], index: usize) -> (&'a [u8], &'a [u8]) { + (&digits[..index], &digits[index..]) +} + +/// Consume until a an invalid digit is found. +/// +/// - `digits` - Slice containing 0 or more digits. +#[inline] +fn consume_digits<'a>(digits: &'a [u8]) -> (&'a [u8], &'a [u8]) { + // Consume all digits. + let mut index = 0; + while index < digits.len() && is_digit(digits[index]) { + index += 1; + } + split_at_index(digits, index) +} + +// Trim leading 0s. +#[inline] +fn ltrim_zero<'a>(bytes: &'a [u8]) -> &'a [u8] { + let count = bytes.iter().take_while(|&&si| si == b'0').count(); + &bytes[count..] +} + +// Trim trailing 0s. +#[inline] +fn rtrim_zero<'a>(bytes: &'a [u8]) -> &'a [u8] { + let count = bytes.iter().rev().take_while(|&&si| si == b'0').count(); + let index = bytes.len() - count; + &bytes[..index] +} + +// PARSERS +// ------- + +/// Parse the exponent of the float. +/// +/// * `exponent` - Slice containing the exponent digits. +/// * `is_positive` - If the exponent sign is positive. +fn parse_exponent(exponent: &[u8], is_positive: bool) -> i32 { + // Parse the sign bit or current data. + let mut value: i32 = 0; + match is_positive { + true => { + for c in exponent { + value = match add_digit_i32(value, to_digit(*c).unwrap()) { + Some(v) => v, + None => return i32::max_value(), + }; + } + }, + false => { + for c in exponent { + value = match sub_digit_i32(value, to_digit(*c).unwrap()) { + Some(v) => v, + None => return i32::min_value(), + }; + } + }, + } + + value +} + +pub fn case_insensitive_starts_with<'a, 'b, Iter1, Iter2>(mut x: Iter1, mut y: Iter2) -> bool +where + Iter1: Iterator<Item = &'a u8>, + Iter2: Iterator<Item = &'b u8>, +{ + // We use a faster optimization here for ASCII letters, which NaN + // and infinite strings **must** be. [A-Z] is 0x41-0x5A, while + // [a-z] is 0x61-0x7A. Therefore, the xor must be 0 or 32 if they + // are case-insensitive equal, but only if at least 1 of the inputs + // is an ASCII letter. + loop { + let yi = y.next(); + if yi.is_none() { + return true; + } + let yi = *yi.unwrap(); + let is_not_equal = x.next().map_or(true, |&xi| { + let xor = xi ^ yi; + xor != 0 && xor != 0x20 + }); + if is_not_equal { + return false; + } + } +} + +/// Parse float from input bytes, returning the float and the remaining bytes. +/// +/// * `bytes` - Array of bytes leading with float-data. +pub fn parse_float<'a, F>(bytes: &'a [u8]) -> (F, &'a [u8]) +where + F: minimal_lexical::Float, +{ + let start = bytes; + + // Parse the sign. + let (is_positive, bytes) = parse_sign(bytes); + + // Check NaN, Inf, Infinity + if case_insensitive_starts_with(bytes.iter(), b"NaN".iter()) { + let mut float = F::from_bits(F::EXPONENT_MASK | (F::HIDDEN_BIT_MASK >> 1)); + if !is_positive { + float = -float; + } + return (float, &bytes[3..]); + } else if case_insensitive_starts_with(bytes.iter(), b"Infinity".iter()) { + let mut float = F::from_bits(F::EXPONENT_MASK); + if !is_positive { + float = -float; + } + return (float, &bytes[8..]); + } else if case_insensitive_starts_with(bytes.iter(), b"inf".iter()) { + let mut float = F::from_bits(F::EXPONENT_MASK); + if !is_positive { + float = -float; + } + return (float, &bytes[3..]); + } + + // Extract and parse the float components: + // 1. Integer + // 2. Fraction + // 3. Exponent + let (integer_slc, bytes) = consume_digits(bytes); + let (fraction_slc, bytes) = match bytes.first() { + Some(&b'.') => consume_digits(&bytes[1..]), + _ => (&bytes[..0], bytes), + }; + let (exponent, bytes) = match bytes.first() { + Some(&b'e') | Some(&b'E') => { + // Extract and parse the exponent. + let (is_positive, bytes) = parse_sign(&bytes[1..]); + let (exponent, bytes) = consume_digits(bytes); + (parse_exponent(exponent, is_positive), bytes) + }, + _ => (0, bytes), + }; + + if bytes.len() == start.len() { + return (F::from_u64(0), bytes); + } + + // Note: You may want to check and validate the float data here: + // 1). Many floats require integer or fraction digits, if a fraction + // is present. + // 2). All floats require either integer or fraction digits. + // 3). Some floats do not allow a '+' sign before the significant digits. + // 4). Many floats require exponent digits after the exponent symbol. + // 5). Some floats do not allow a '+' sign before the exponent. + + // We now need to trim leading and trailing 0s from the integer + // and fraction, respectively. This is required to make the + // fast and moderate paths more efficient, and for the slow + // path. + let integer_slc = ltrim_zero(integer_slc); + let fraction_slc = rtrim_zero(fraction_slc); + + // Create the float and return our data. + let mut float: F = + minimal_lexical::parse_float(integer_slc.iter(), fraction_slc.iter(), exponent); + if !is_positive { + float = -float; + } + + (float, bytes) +} + +macro_rules! b { + ($x:literal) => { + $x.as_bytes() + }; +} + +#[test] +fn f32_test() { + assert_eq!( + (184467440000000000000.0, b!("\x00\x00006")), + parse_float::<f32>(b"000184467440737095516150\x00\x00006") + ); +} + +#[test] +fn f64_test() { + assert_eq!( + (184467440737095500000.0, b!("\x00\x00006")), + parse_float::<f64>(b"000184467440737095516150\x00\x00006") + ); +} diff --git a/third_party/rust/minimal-lexical/tests/lemire_tests.rs b/third_party/rust/minimal-lexical/tests/lemire_tests.rs new file mode 100644 index 0000000000..0523ca5b2a --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/lemire_tests.rs @@ -0,0 +1,378 @@ +//! These tests are adapted from the Rust core library's unittests. + +#![cfg(not(feature = "compact"))] + +use minimal_lexical::lemire; +use minimal_lexical::num::Float; + +fn compute_error32(q: i32, w: u64) -> (i32, u64) { + let fp = lemire::compute_error::<f32>(q, w); + (fp.exp, fp.mant) +} + +fn compute_error64(q: i32, w: u64) -> (i32, u64) { + let fp = lemire::compute_error::<f64>(q, w); + (fp.exp, fp.mant) +} + +fn compute_error_scaled32(q: i32, w: u64, lz: i32) -> (i32, u64) { + let fp = lemire::compute_error_scaled::<f32>(q, w, lz); + (fp.exp, fp.mant) +} + +fn compute_error_scaled64(q: i32, w: u64, lz: i32) -> (i32, u64) { + let fp = lemire::compute_error_scaled::<f64>(q, w, lz); + (fp.exp, fp.mant) +} + +fn compute_float32(q: i32, w: u64) -> (i32, u64) { + let fp = lemire::compute_float::<f32>(q, w); + (fp.exp, fp.mant) +} + +fn compute_float64(q: i32, w: u64) -> (i32, u64) { + let fp = lemire::compute_float::<f64>(q, w); + (fp.exp, fp.mant) +} + +#[test] +fn compute_error32_test() { + // These test near-halfway cases for single-precision floats. + assert_eq!(compute_error32(0, 16777216), (111 + f32::INVALID_FP, 9223372036854775808)); + assert_eq!(compute_error32(0, 16777217), (111 + f32::INVALID_FP, 9223372586610589696)); + assert_eq!(compute_error32(0, 16777218), (111 + f32::INVALID_FP, 9223373136366403584)); + assert_eq!(compute_error32(0, 16777219), (111 + f32::INVALID_FP, 9223373686122217472)); + assert_eq!(compute_error32(0, 16777220), (111 + f32::INVALID_FP, 9223374235878031360)); + + // These are examples of the above tests, with + // digits from the exponent shifted to the mantissa. + assert_eq!( + compute_error32(-10, 167772160000000000), + (111 + f32::INVALID_FP, 9223372036854775808) + ); + assert_eq!( + compute_error32(-10, 167772170000000000), + (111 + f32::INVALID_FP, 9223372586610589696) + ); + assert_eq!( + compute_error32(-10, 167772180000000000), + (111 + f32::INVALID_FP, 9223373136366403584) + ); + // Let's check the lines to see if anything is different in table... + assert_eq!( + compute_error32(-10, 167772190000000000), + (111 + f32::INVALID_FP, 9223373686122217472) + ); + assert_eq!( + compute_error32(-10, 167772200000000000), + (111 + f32::INVALID_FP, 9223374235878031360) + ); +} + +#[test] +fn compute_error64_test() { + // These test near-halfway cases for double-precision floats. + assert_eq!(compute_error64(0, 9007199254740992), (1065 + f64::INVALID_FP, 9223372036854775808)); + assert_eq!(compute_error64(0, 9007199254740993), (1065 + f64::INVALID_FP, 9223372036854776832)); + assert_eq!(compute_error64(0, 9007199254740994), (1065 + f64::INVALID_FP, 9223372036854777856)); + assert_eq!(compute_error64(0, 9007199254740995), (1065 + f64::INVALID_FP, 9223372036854778880)); + assert_eq!(compute_error64(0, 9007199254740996), (1065 + f64::INVALID_FP, 9223372036854779904)); + assert_eq!( + compute_error64(0, 18014398509481984), + (1066 + f64::INVALID_FP, 9223372036854775808) + ); + assert_eq!( + compute_error64(0, 18014398509481986), + (1066 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error64(0, 18014398509481988), + (1066 + f64::INVALID_FP, 9223372036854777856) + ); + assert_eq!( + compute_error64(0, 18014398509481990), + (1066 + f64::INVALID_FP, 9223372036854778880) + ); + assert_eq!( + compute_error64(0, 18014398509481992), + (1066 + f64::INVALID_FP, 9223372036854779904) + ); + + // Test a much closer set of examples. + assert_eq!( + compute_error64(0, 9007199254740991), + (1064 + f64::INVALID_FP, 18446744073709549568) + ); + assert_eq!( + compute_error64(0, 9223372036854776831), + (1075 + f64::INVALID_FP, 9223372036854776830) + ); + assert_eq!( + compute_error64(0, 9223372036854776832), + (1075 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error64(0, 9223372036854776833), + (1075 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error64(-42, 9123456727292927), + (925 + f64::INVALID_FP, 13021432563531497894) + ); + assert_eq!( + compute_error64(-43, 91234567272929275), + (925 + f64::INVALID_FP, 13021432563531498606) + ); + assert_eq!( + compute_error64(-42, 9123456727292928), + (925 + f64::INVALID_FP, 13021432563531499320) + ); + + // These are examples of the above tests, with + // digits from the exponent shifted to the mantissa. + assert_eq!( + compute_error64(-3, 9007199254740992000), + (1065 + f64::INVALID_FP, 9223372036854775808) + ); + assert_eq!( + compute_error64(-3, 9007199254740993000), + (1065 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error64(-3, 9007199254740994000), + (1065 + f64::INVALID_FP, 9223372036854777856) + ); + assert_eq!( + compute_error64(-3, 9007199254740995000), + (1065 + f64::INVALID_FP, 9223372036854778880) + ); + assert_eq!( + compute_error64(-3, 9007199254740996000), + (1065 + f64::INVALID_FP, 9223372036854779904) + ); + + // Test from errors in atof. + assert_eq!( + compute_error64(-18, 1000000178813934326), + (1012 + f64::INVALID_FP, 9223373686122217470) + ); + + // Check edge-cases from previous errors. + assert_eq!( + compute_error64(-342, 2470328229206232720), + (-64 + f64::INVALID_FP, 18446744073709551608) + ); +} + +#[test] +fn compute_error_scaled32_test() { + // These are the same examples above, just using pre-computed scaled values. + + // These test near-halfway cases for single-precision floats. + assert_eq!( + compute_error_scaled32(0, 4611686018427387904, 39), + (111 + f32::INVALID_FP, 9223372036854775808) + ); + assert_eq!( + compute_error_scaled32(0, 4611686293305294848, 39), + (111 + f32::INVALID_FP, 9223372586610589696) + ); + assert_eq!( + compute_error_scaled32(0, 4611686568183201792, 39), + (111 + f32::INVALID_FP, 9223373136366403584) + ); + assert_eq!( + compute_error_scaled32(0, 4611686843061108736, 39), + (111 + f32::INVALID_FP, 9223373686122217472) + ); + assert_eq!( + compute_error_scaled32(0, 4611687117939015680, 39), + (111 + f32::INVALID_FP, 9223374235878031360) + ); + + assert_eq!( + compute_error_scaled32(-10, 9223372036854775808, 6), + (111 + f32::INVALID_FP, 9223372036854775808) + ); + assert_eq!( + compute_error_scaled32(-10, 9223372586610589696, 6), + (111 + f32::INVALID_FP, 9223372586610589696) + ); + assert_eq!( + compute_error_scaled32(-10, 9223373136366403584, 6), + (111 + f32::INVALID_FP, 9223373136366403584) + ); + assert_eq!( + compute_error_scaled32(-10, 9223373686122217472, 6), + (111 + f32::INVALID_FP, 9223373686122217472) + ); + assert_eq!( + compute_error_scaled32(-10, 9223374235878031360, 6), + (111 + f32::INVALID_FP, 9223374235878031360) + ); +} + +#[test] +fn compute_error_scaled64_test() { + // These are the same examples above, just using pre-computed scaled values. + + // These test near-halfway cases for double-precision floats. + assert_eq!( + compute_error_scaled64(0, 4611686018427387904, 10), + (1065 + f64::INVALID_FP, 9223372036854775808) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427388416, 10), + (1065 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427388928, 10), + (1065 + f64::INVALID_FP, 9223372036854777856) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427389440, 10), + (1065 + f64::INVALID_FP, 9223372036854778880) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427389952, 10), + (1065 + f64::INVALID_FP, 9223372036854779904) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427387904, 9), + (1066 + f64::INVALID_FP, 9223372036854775808) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427388416, 9), + (1066 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427388928, 9), + (1066 + f64::INVALID_FP, 9223372036854777856) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427389440, 9), + (1066 + f64::INVALID_FP, 9223372036854778880) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427389952, 9), + (1066 + f64::INVALID_FP, 9223372036854779904) + ); + + // Test a much closer set of examples. + assert_eq!( + compute_error_scaled64(0, 9223372036854774784, 11), + (1064 + f64::INVALID_FP, 18446744073709549568) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427388415, 0), + (1075 + f64::INVALID_FP, 9223372036854776830) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427388416, 0), + (1075 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error_scaled64(0, 4611686018427388416, 0), + (1075 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error_scaled64(-42, 6510716281765748947, 10), + (925 + f64::INVALID_FP, 13021432563531497894) + ); + assert_eq!( + compute_error_scaled64(-43, 6510716281765749303, 7), + (925 + f64::INVALID_FP, 13021432563531498606) + ); + assert_eq!( + compute_error_scaled64(-42, 6510716281765749660, 10), + (925 + f64::INVALID_FP, 13021432563531499320) + ); + + // These are examples of the above tests, with + // digits from the exponent shifted to the mantissa. + assert_eq!( + compute_error_scaled64(-3, 9223372036854775808, 1), + (1065 + f64::INVALID_FP, 9223372036854775808) + ); + assert_eq!( + compute_error_scaled64(-3, 9223372036854776832, 1), + (1065 + f64::INVALID_FP, 9223372036854776832) + ); + assert_eq!( + compute_error_scaled64(-3, 9223372036854777856, 1), + (1065 + f64::INVALID_FP, 9223372036854777856) + ); + assert_eq!( + compute_error_scaled64(-3, 9223372036854778880, 1), + (1065 + f64::INVALID_FP, 9223372036854778880) + ); + assert_eq!( + compute_error_scaled64(-3, 9223372036854779904, 1), + (1065 + f64::INVALID_FP, 9223372036854779904) + ); + + // Test from errors in atof. + assert_eq!( + compute_error_scaled64(-18, 9223373686122217470, 4), + (1012 + f64::INVALID_FP, 9223373686122217470) + ); + + // Check edge-cases from previous errors. + assert_eq!( + compute_error_scaled64(-342, 9223372036854775804, 2), + (-64 + f64::INVALID_FP, 18446744073709551608) + ); +} + +#[test] +fn compute_float_f32_rounding() { + // These test near-halfway cases for single-precision floats. + assert_eq!(compute_float32(0, 16777216), (151, 0)); + assert_eq!(compute_float32(0, 16777217), (151, 0)); + assert_eq!(compute_float32(0, 16777218), (151, 1)); + assert_eq!(compute_float32(0, 16777219), (151, 2)); + assert_eq!(compute_float32(0, 16777220), (151, 2)); + + // These are examples of the above tests, with + // digits from the exponent shifted to the mantissa. + assert_eq!(compute_float32(-10, 167772160000000000), (151, 0)); + assert_eq!(compute_float32(-10, 167772170000000000), (151, 0)); + assert_eq!(compute_float32(-10, 167772180000000000), (151, 1)); + // Let's check the lines to see if anything is different in table... + assert_eq!(compute_float32(-10, 167772190000000000), (151, 2)); + assert_eq!(compute_float32(-10, 167772200000000000), (151, 2)); +} + +#[test] +fn compute_float_f64_rounding() { + // Also need to check halfway cases **inside** that exponent range. + + // These test near-halfway cases for double-precision floats. + assert_eq!(compute_float64(0, 9007199254740992), (1076, 0)); + assert_eq!(compute_float64(0, 9007199254740993), (1076, 0)); + assert_eq!(compute_float64(0, 9007199254740994), (1076, 1)); + assert_eq!(compute_float64(0, 9007199254740995), (1076, 2)); + assert_eq!(compute_float64(0, 9007199254740996), (1076, 2)); + assert_eq!(compute_float64(0, 18014398509481984), (1077, 0)); + assert_eq!(compute_float64(0, 18014398509481986), (1077, 0)); + assert_eq!(compute_float64(0, 18014398509481988), (1077, 1)); + assert_eq!(compute_float64(0, 18014398509481990), (1077, 2)); + assert_eq!(compute_float64(0, 18014398509481992), (1077, 2)); + + // Test a much closer set of examples. + assert_eq!(compute_float64(0, 9007199254740991), (1075, 4503599627370495)); + assert_eq!(compute_float64(0, 9223372036854776831), (1086, 0)); + assert_eq!(compute_float64(0, 9223372036854776832), (1086, 0)); + assert_eq!(compute_float64(0, 9223372036854776833), (1086, 1)); + assert_eq!(compute_float64(-42, 9123456727292927), (936, 1854521741541368)); + assert_eq!(compute_float64(-43, 91234567272929275), (936, 1854521741541369)); + assert_eq!(compute_float64(-42, 9123456727292928), (936, 1854521741541369)); + + // These are examples of the above tests, with + // digits from the exponent shifted to the mantissa. + assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0)); + assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0)); + assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1)); + assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2)); + assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2)); +} diff --git a/third_party/rust/minimal-lexical/tests/libm_tests.rs b/third_party/rust/minimal-lexical/tests/libm_tests.rs new file mode 100644 index 0000000000..7f5352e193 --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/libm_tests.rs @@ -0,0 +1,289 @@ +#![cfg(all(not(feature = "std"), feature = "compact"))] + +// These are adapted from libm, a port of musl libc's libm to Rust. +// libm can be found online [here](https://github.com/rust-lang/libm), +// and is similarly licensed under an Apache2.0/MIT license + +use core::f64; +use minimal_lexical::libm; + +#[test] +fn fabsf_sanity_test() { + assert_eq!(libm::fabsf(-1.0), 1.0); + assert_eq!(libm::fabsf(2.8), 2.8); +} + +/// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs +#[test] +fn fabsf_spec_test() { + assert!(libm::fabsf(f32::NAN).is_nan()); + for f in [0.0, -0.0].iter().copied() { + assert_eq!(libm::fabsf(f), 0.0); + } + for f in [f32::INFINITY, f32::NEG_INFINITY].iter().copied() { + assert_eq!(libm::fabsf(f), f32::INFINITY); + } +} + +#[test] +fn sqrtf_sanity_test() { + assert_eq!(libm::sqrtf(100.0), 10.0); + assert_eq!(libm::sqrtf(4.0), 2.0); +} + +/// The spec: https://en.cppreference.com/w/cpp/numeric/math/sqrt +#[test] +fn sqrtf_spec_test() { + // Not Asserted: FE_INVALID exception is raised if argument is negative. + assert!(libm::sqrtf(-1.0).is_nan()); + assert!(libm::sqrtf(f32::NAN).is_nan()); + for f in [0.0, -0.0, f32::INFINITY].iter().copied() { + assert_eq!(libm::sqrtf(f), f); + } +} + +const POS_ZERO: &[f64] = &[0.0]; +const NEG_ZERO: &[f64] = &[-0.0]; +const POS_ONE: &[f64] = &[1.0]; +const NEG_ONE: &[f64] = &[-1.0]; +const POS_FLOATS: &[f64] = &[99.0 / 70.0, f64::consts::E, f64::consts::PI]; +const NEG_FLOATS: &[f64] = &[-99.0 / 70.0, -f64::consts::E, -f64::consts::PI]; +const POS_SMALL_FLOATS: &[f64] = &[(1.0 / 2.0), f64::MIN_POSITIVE, f64::EPSILON]; +const NEG_SMALL_FLOATS: &[f64] = &[-(1.0 / 2.0), -f64::MIN_POSITIVE, -f64::EPSILON]; +const POS_EVENS: &[f64] = &[2.0, 6.0, 8.0, 10.0, 22.0, 100.0, f64::MAX]; +const NEG_EVENS: &[f64] = &[f64::MIN, -100.0, -22.0, -10.0, -8.0, -6.0, -2.0]; +const POS_ODDS: &[f64] = &[3.0, 7.0]; +const NEG_ODDS: &[f64] = &[-7.0, -3.0]; +const NANS: &[f64] = &[f64::NAN]; +const POS_INF: &[f64] = &[f64::INFINITY]; +const NEG_INF: &[f64] = &[f64::NEG_INFINITY]; + +const ALL: &[&[f64]] = &[ + POS_ZERO, + NEG_ZERO, + NANS, + NEG_SMALL_FLOATS, + POS_SMALL_FLOATS, + NEG_FLOATS, + POS_FLOATS, + NEG_EVENS, + POS_EVENS, + NEG_ODDS, + POS_ODDS, + NEG_INF, + POS_INF, + NEG_ONE, + POS_ONE, +]; +const POS: &[&[f64]] = &[POS_ZERO, POS_ODDS, POS_ONE, POS_FLOATS, POS_EVENS, POS_INF]; +const NEG: &[&[f64]] = &[NEG_ZERO, NEG_ODDS, NEG_ONE, NEG_FLOATS, NEG_EVENS, NEG_INF]; + +fn powd(base: f64, exponent: f64, expected: f64) { + let res = libm::powd(base, exponent); + assert!( + if expected.is_nan() { + res.is_nan() + } else { + libm::powd(base, exponent) == expected + }, + "{} ** {} was {} instead of {}", + base, + exponent, + res, + expected + ); +} + +fn powd_test_sets_as_base(sets: &[&[f64]], exponent: f64, expected: f64) { + sets.iter().for_each(|s| s.iter().for_each(|val| powd(*val, exponent, expected))); +} + +fn powd_test_sets_as_exponent(base: f64, sets: &[&[f64]], expected: f64) { + sets.iter().for_each(|s| s.iter().for_each(|val| powd(base, *val, expected))); +} + +fn powd_test_sets(sets: &[&[f64]], computed: &dyn Fn(f64) -> f64, expected: &dyn Fn(f64) -> f64) { + sets.iter().for_each(|s| { + s.iter().for_each(|val| { + let exp = expected(*val); + let res = computed(*val); + + assert!( + if exp.is_nan() { + res.is_nan() + } else { + exp == res + }, + "test for {} was {} instead of {}", + val, + res, + exp + ); + }) + }); +} + +#[test] +fn powd_zero_as_exponent() { + powd_test_sets_as_base(ALL, 0.0, 1.0); + powd_test_sets_as_base(ALL, -0.0, 1.0); +} + +#[test] +fn powd_one_as_base() { + powd_test_sets_as_exponent(1.0, ALL, 1.0); +} + +#[test] +fn powd_nan_inputs() { + // NAN as the base: + // (NAN ^ anything *but 0* should be NAN) + powd_test_sets_as_exponent(f64::NAN, &ALL[2..], f64::NAN); + + // NAN as the exponent: + // (anything *but 1* ^ NAN should be NAN) + powd_test_sets_as_base(&ALL[..(ALL.len() - 2)], f64::NAN, f64::NAN); +} + +#[test] +fn powd_infinity_as_base() { + // Positive Infinity as the base: + // (+Infinity ^ positive anything but 0 and NAN should be +Infinity) + powd_test_sets_as_exponent(f64::INFINITY, &POS[1..], f64::INFINITY); + + // (+Infinity ^ negative anything except 0 and NAN should be 0.0) + powd_test_sets_as_exponent(f64::INFINITY, &NEG[1..], 0.0); + + // Negative Infinity as the base: + // (-Infinity ^ positive odd ints should be -Infinity) + powd_test_sets_as_exponent(f64::NEG_INFINITY, &[POS_ODDS], f64::NEG_INFINITY); + + // (-Infinity ^ anything but odd ints should be == -0 ^ (-anything)) + // We can lump in pos/neg odd ints here because they don't seem to + // cause panics (div by zero) in release mode (I think). + powd_test_sets(ALL, &|v: f64| libm::powd(f64::NEG_INFINITY, v), &|v: f64| libm::powd(-0.0, -v)); +} + +#[test] +fn infinity_as_exponent() { + // Positive/Negative base greater than 1: + // (pos/neg > 1 ^ Infinity should be Infinity - note this excludes NAN as the base) + powd_test_sets_as_base(&ALL[5..(ALL.len() - 2)], f64::INFINITY, f64::INFINITY); + + // (pos/neg > 1 ^ -Infinity should be 0.0) + powd_test_sets_as_base(&ALL[5..ALL.len() - 2], f64::NEG_INFINITY, 0.0); + + // Positive/Negative base less than 1: + let base_below_one = &[POS_ZERO, NEG_ZERO, NEG_SMALL_FLOATS, POS_SMALL_FLOATS]; + + // (pos/neg < 1 ^ Infinity should be 0.0 - this also excludes NAN as the base) + powd_test_sets_as_base(base_below_one, f64::INFINITY, 0.0); + + // (pos/neg < 1 ^ -Infinity should be Infinity) + powd_test_sets_as_base(base_below_one, f64::NEG_INFINITY, f64::INFINITY); + + // Positive/Negative 1 as the base: + // (pos/neg 1 ^ Infinity should be 1) + powd_test_sets_as_base(&[NEG_ONE, POS_ONE], f64::INFINITY, 1.0); + + // (pos/neg 1 ^ -Infinity should be 1) + powd_test_sets_as_base(&[NEG_ONE, POS_ONE], f64::NEG_INFINITY, 1.0); +} + +#[test] +fn powd_zero_as_base() { + // Positive Zero as the base: + // (+0 ^ anything positive but 0 and NAN should be +0) + powd_test_sets_as_exponent(0.0, &POS[1..], 0.0); + + // (+0 ^ anything negative but 0 and NAN should be Infinity) + // (this should panic because we're dividing by zero) + powd_test_sets_as_exponent(0.0, &NEG[1..], f64::INFINITY); + + // Negative Zero as the base: + // (-0 ^ anything positive but 0, NAN, and odd ints should be +0) + powd_test_sets_as_exponent(-0.0, &POS[3..], 0.0); + + // (-0 ^ anything negative but 0, NAN, and odd ints should be Infinity) + // (should panic because of divide by zero) + powd_test_sets_as_exponent(-0.0, &NEG[3..], f64::INFINITY); + + // (-0 ^ positive odd ints should be -0) + powd_test_sets_as_exponent(-0.0, &[POS_ODDS], -0.0); + + // (-0 ^ negative odd ints should be -Infinity) + // (should panic because of divide by zero) + powd_test_sets_as_exponent(-0.0, &[NEG_ODDS], f64::NEG_INFINITY); +} + +#[test] +fn special_cases() { + // One as the exponent: + // (anything ^ 1 should be anything - i.e. the base) + powd_test_sets(ALL, &|v: f64| libm::powd(v, 1.0), &|v: f64| v); + + // Negative One as the exponent: + // (anything ^ -1 should be 1/anything) + powd_test_sets(ALL, &|v: f64| libm::powd(v, -1.0), &|v: f64| 1.0 / v); + + // Factoring -1 out: + // (negative anything ^ integer should be (-1 ^ integer) * (positive anything ^ integer)) + [POS_ZERO, NEG_ZERO, POS_ONE, NEG_ONE, POS_EVENS, NEG_EVENS].iter().for_each(|int_set| { + int_set.iter().for_each(|int| { + powd_test_sets(ALL, &|v: f64| libm::powd(-v, *int), &|v: f64| { + libm::powd(-1.0, *int) * libm::powd(v, *int) + }); + }) + }); + + // Negative base (imaginary results): + // (-anything except 0 and Infinity ^ non-integer should be NAN) + NEG[1..(NEG.len() - 1)].iter().for_each(|set| { + set.iter().for_each(|val| { + powd_test_sets(&ALL[3..7], &|v: f64| libm::powd(*val, v), &|_| f64::NAN); + }) + }); +} + +#[test] +fn normal_cases() { + assert_eq!(libm::powd(2.0, 20.0), (1 << 20) as f64); + assert_eq!(libm::powd(-1.0, 9.0), -1.0); + assert!(libm::powd(-1.0, 2.2).is_nan()); + assert!(libm::powd(-1.0, -1.14).is_nan()); +} + +#[test] +fn fabsd_sanity_test() { + assert_eq!(libm::fabsd(-1.0), 1.0); + assert_eq!(libm::fabsd(2.8), 2.8); +} + +/// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs +#[test] +fn fabsd_spec_test() { + assert!(libm::fabsd(f64::NAN).is_nan()); + for f in [0.0, -0.0].iter().copied() { + assert_eq!(libm::fabsd(f), 0.0); + } + for f in [f64::INFINITY, f64::NEG_INFINITY].iter().copied() { + assert_eq!(libm::fabsd(f), f64::INFINITY); + } +} + +#[test] +fn sqrtd_sanity_test() { + assert_eq!(libm::sqrtd(100.0), 10.0); + assert_eq!(libm::sqrtd(4.0), 2.0); +} + +/// The spec: https://en.cppreference.com/w/cpp/numeric/math/sqrt +#[test] +fn sqrtd_spec_test() { + // Not Asserted: FE_INVALID exception is raised if argument is negative. + assert!(libm::sqrtd(-1.0).is_nan()); + assert!(libm::sqrtd(f64::NAN).is_nan()); + for f in [0.0, -0.0, f64::INFINITY].iter().copied() { + assert_eq!(libm::sqrtd(f), f); + } +} diff --git a/third_party/rust/minimal-lexical/tests/mask_tests.rs b/third_party/rust/minimal-lexical/tests/mask_tests.rs new file mode 100644 index 0000000000..97e70a72b8 --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/mask_tests.rs @@ -0,0 +1,16 @@ +use minimal_lexical::mask; + +#[test] +fn lower_n_mask_test() { + assert_eq!(mask::lower_n_mask(2), 0b11); +} + +#[test] +fn lower_n_halfway_test() { + assert_eq!(mask::lower_n_halfway(2), 0b10); +} + +#[test] +fn nth_bit_test() { + assert_eq!(mask::nth_bit(2), 0b100); +} diff --git a/third_party/rust/minimal-lexical/tests/number_tests.rs b/third_party/rust/minimal-lexical/tests/number_tests.rs new file mode 100644 index 0000000000..947be394c9 --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/number_tests.rs @@ -0,0 +1,88 @@ +use minimal_lexical::number::Number; + +#[test] +fn is_fast_path_test() { + let mut number = Number { + exponent: -4, + mantissa: 12345, + many_digits: false, + }; + assert_eq!(number.is_fast_path::<f32>(), true); + assert_eq!(number.is_fast_path::<f64>(), true); + + number.exponent = -15; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), true); + + number.exponent = -25; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), false); + + number.exponent = 25; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), true); + + number.exponent = 36; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), true); + + number.exponent = 38; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), false); + + number.mantissa = 1 << 25; + number.exponent = 0; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), true); + + number.mantissa = 1 << 54; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), false); + + number.mantissa = 1 << 52; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), true); + + number.many_digits = true; + assert_eq!(number.is_fast_path::<f32>(), false); + assert_eq!(number.is_fast_path::<f64>(), false); +} + +#[test] +fn try_fast_path_test() { + let mut number = Number { + exponent: -4, + mantissa: 12345, + many_digits: false, + }; + assert_eq!(number.try_fast_path::<f32>(), Some(1.2345)); + assert_eq!(number.try_fast_path::<f64>(), Some(1.2345)); + + number.exponent = -10; + assert_eq!(number.try_fast_path::<f32>(), Some(1.2345e-6)); + assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e-6)); + + number.exponent = -20; + assert_eq!(number.try_fast_path::<f32>(), None); + assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e-16)); + + number.exponent = -25; + assert_eq!(number.try_fast_path::<f32>(), None); + assert_eq!(number.try_fast_path::<f64>(), None); + + number.exponent = 12; + assert_eq!(number.try_fast_path::<f32>(), Some(1.2345e16)); + assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e16)); + + number.exponent = 25; + assert_eq!(number.try_fast_path::<f32>(), None); + assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e29)); + + number.exponent = 32; + assert_eq!(number.try_fast_path::<f32>(), None); + assert_eq!(number.try_fast_path::<f64>(), Some(1.2345e36)); + + number.exponent = 36; + assert_eq!(number.try_fast_path::<f32>(), None); + assert_eq!(number.try_fast_path::<f64>(), None); +} diff --git a/third_party/rust/minimal-lexical/tests/parse_tests.rs b/third_party/rust/minimal-lexical/tests/parse_tests.rs new file mode 100644 index 0000000000..48856fd1cc --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/parse_tests.rs @@ -0,0 +1,189 @@ +use core::f64; +use minimal_lexical::{num, parse}; + +fn check_parse_float<F: num::Float>(integer: &str, fraction: &str, exponent: i32, expected: F) { + let integer = integer.as_bytes().iter(); + let fraction = fraction.as_bytes().iter(); + assert!(expected == parse::parse_float::<F, _, _>(integer, fraction, exponent)); +} + +#[test] +fn parse_f32_test() { + check_parse_float("", "", 0, 0.0_f32); + check_parse_float("1", "2345", 0, 1.2345_f32); + check_parse_float("12", "345", 0, 12.345_f32); + check_parse_float("12345", "6789", 0, 12345.6789_f32); + check_parse_float("1", "2345", 10, 1.2345e10_f32); + check_parse_float("1", "2345", -38, 1.2345e-38_f32); + + // Check expected rounding, using borderline cases. + // Round-down, halfway + check_parse_float("16777216", "", 0, 16777216.0_f32); + check_parse_float("16777217", "", 0, 16777216.0_f32); + check_parse_float("16777218", "", 0, 16777218.0_f32); + check_parse_float("33554432", "", 0, 33554432.0_f32); + check_parse_float("33554434", "", 0, 33554432.0_f32); + check_parse_float("33554436", "", 0, 33554436.0_f32); + check_parse_float("17179869184", "", 0, 17179869184.0_f32); + check_parse_float("17179870208", "", 0, 17179869184.0_f32); + check_parse_float("17179871232", "", 0, 17179871232.0_f32); + + // Round-up, halfway + check_parse_float("16777218", "", 0, 16777218.0_f32); + check_parse_float("16777219", "", 0, 16777220.0_f32); + check_parse_float("16777220", "", 0, 16777220.0_f32); + + check_parse_float("33554436", "", 0, 33554436.0_f32); + check_parse_float("33554438", "", 0, 33554440.0_f32); + check_parse_float("33554440", "", 0, 33554440.0_f32); + + check_parse_float("17179871232", "", 0, 17179871232.0_f32); + check_parse_float("17179872256", "", 0, 17179873280.0_f32); + check_parse_float("17179873280", "", 0, 17179873280.0_f32); + + // Round-up, above halfway + check_parse_float("33554435", "", 0, 33554436.0_f32); + check_parse_float("17179870209", "", 0, 17179871232.0_f32); + + // Check exactly halfway, round-up at halfway + check_parse_float("1", "00000017881393432617187499", 0, 1.0000001_f32); + check_parse_float("1", "000000178813934326171875", 0, 1.0000002_f32); + check_parse_float("1", "00000017881393432617187501", 0, 1.0000002_f32); + + check_parse_float("", "000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625", 0, 1.1754943508222875e-38f32); +} + +#[test] +fn parse_f64_test() { + check_parse_float("", "", 0, 0.0_f64); + check_parse_float("1", "2345", 0, 1.2345_f64); + check_parse_float("12", "345", 0, 12.345_f64); + check_parse_float("12345", "6789", 0, 12345.6789_f64); + check_parse_float("1", "2345", 10, 1.2345e10_f64); + check_parse_float("1", "2345", -308, 1.2345e-308_f64); + + // Check expected rounding, using borderline cases. + // Round-down, halfway + check_parse_float("9007199254740992", "", 0, 9007199254740992.0_f64); + check_parse_float("9007199254740993", "", 0, 9007199254740992.0_f64); + check_parse_float("9007199254740994", "", 0, 9007199254740994.0_f64); + + check_parse_float("18014398509481984", "", 0, 18014398509481984.0_f64); + check_parse_float("18014398509481986", "", 0, 18014398509481984.0_f64); + check_parse_float("18014398509481988", "", 0, 18014398509481988.0_f64); + + check_parse_float("9223372036854775808", "", 0, 9223372036854775808.0_f64); + check_parse_float("9223372036854776832", "", 0, 9223372036854775808.0_f64); + check_parse_float("9223372036854777856", "", 0, 9223372036854777856.0_f64); + + check_parse_float( + "11417981541647679048466287755595961091061972992", + "", + 0, + 11417981541647679048466287755595961091061972992.0_f64, + ); + check_parse_float( + "11417981541647680316116887983825362587765178368", + "", + 0, + 11417981541647679048466287755595961091061972992.0_f64, + ); + check_parse_float( + "11417981541647681583767488212054764084468383744", + "", + 0, + 11417981541647681583767488212054764084468383744.0_f64, + ); + + // Round-up, halfway + check_parse_float("9007199254740994", "", 0, 9007199254740994.0_f64); + check_parse_float("9007199254740995", "", 0, 9007199254740996.0_f64); + check_parse_float("9007199254740996", "", 0, 9007199254740996.0_f64); + + check_parse_float("18014398509481988", "", 0, 18014398509481988.0_f64); + check_parse_float("18014398509481990", "", 0, 18014398509481992.0_f64); + check_parse_float("18014398509481992", "", 0, 18014398509481992.0_f64); + + check_parse_float("9223372036854777856", "", 0, 9223372036854777856.0_f64); + check_parse_float("9223372036854778880", "", 0, 9223372036854779904.0_f64); + check_parse_float("9223372036854779904", "", 0, 9223372036854779904.0_f64); + + check_parse_float( + "11417981541647681583767488212054764084468383744", + "", + 0, + 11417981541647681583767488212054764084468383744.0_f64, + ); + check_parse_float( + "11417981541647682851418088440284165581171589120", + "", + 0, + 11417981541647684119068688668513567077874794496.0_f64, + ); + check_parse_float( + "11417981541647684119068688668513567077874794496", + "", + 0, + 11417981541647684119068688668513567077874794496.0_f64, + ); + + // Round-up, above halfway + check_parse_float("9223372036854776833", "", 0, 9223372036854777856.0_f64); + check_parse_float( + "11417981541647680316116887983825362587765178369", + "", + 0, + 11417981541647681583767488212054764084468383744.0_f64, + ); + + // Rounding error + // Adapted from failures in strtod. + check_parse_float("2", "2250738585072014", -308, 2.2250738585072014e-308_f64); + check_parse_float("2", "2250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350879141491589130396211068700864386945946455276572074078206217433799881410632673292535522868813721490129811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050510806099407302629371289589500035837999672072543043602840788957717961509455167482434710307026091446215722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704275671864433837704860378616227717385456230658746790140867233276367187499", -308, 2.225073858507201e-308_f64); + check_parse_float("2", "22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901298112245145188984905722230728525513315575501591439747639798341180199932396254828901710708185069063066665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462157228988025818254518032570701886087211312807951223342628836862232150377566662250398253433597456888442390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042756718644338377048603786162277173854562306587467901408672332763671875", -308, 2.2250738585072014e-308_f64); + check_parse_float("2", "2250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350879141491589130396211068700864386945946455276572074078206217433799881410632673292535522868813721490129811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050510806099407302629371289589500035837999672072543043602840788957717961509455167482434710307026091446215722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704275671864433837704860378616227717385456230658746790140867233276367187501", -308, 2.2250738585072014e-308_f64); + check_parse_float("179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497791", "9999999999999999999999999999999999999999999999999999999999999999999999", 0, 1.7976931348623157e+308_f64); + check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984374999", -324, 5.0e-324_f64); + check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375", -324, 1.0e-323_f64); + check_parse_float("7", "4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375001", -324, 1.0e-323_f64); + check_parse_float("", "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328125", 0, 0.0_f64); + + // Rounding error + // Adapted from: + // https://www.exploringbinary.com/how-glibc-strtod-works/ + check_parse_float("", "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0, 2.2250738585072011e-308_f64); + + // Rounding error + // Adapted from test-parse-random failures. + check_parse_float("1009", "", -31, 1.009e-28_f64); + check_parse_float("18294", "", 304, f64::INFINITY); + + // Rounding error + // Adapted from a @dangrabcad's issue #20. + check_parse_float("7", "689539722041643", 164, 7.689539722041643e164_f64); + check_parse_float("768953972204164300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", 0, 7.689539722041643e164_f64); + check_parse_float("768953972204164300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 7.689539722041643e164_f64); + + // Check other cases similar to @dangrabcad's issue #20. + check_parse_float("9223372036854776833", "0", 0, 9223372036854777856.0_f64); + check_parse_float( + "11417981541647680316116887983825362587765178369", + "0", + 0, + 11417981541647681583767488212054764084468383744.0_f64, + ); + check_parse_float("9007199254740995", "0", 0, 9007199254740996.0_f64); + check_parse_float("18014398509481990", "0", 0, 18014398509481992.0_f64); + check_parse_float("9223372036854778880", "0", 0, 9223372036854779904.0_f64); + check_parse_float( + "11417981541647682851418088440284165581171589120", + "0", + 0, + 11417981541647684119068688668513567077874794496.0_f64, + ); + + // Check other cases ostensibly identified via proptest. + check_parse_float("71610528364411830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 71610528364411830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64); + check_parse_float("126769393745745060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 126769393745745060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64); + check_parse_float("38652960461239320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0", 0, 38652960461239320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0_f64); +} diff --git a/third_party/rust/minimal-lexical/tests/rounding_tests.rs b/third_party/rust/minimal-lexical/tests/rounding_tests.rs new file mode 100644 index 0000000000..794d696fb8 --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/rounding_tests.rs @@ -0,0 +1,64 @@ +use minimal_lexical::extended_float::ExtendedFloat; +use minimal_lexical::rounding; + +#[test] +fn round_test() { + let mut fp = ExtendedFloat { + mant: 9223372036854776832, + exp: -10, + }; + rounding::round::<f64, _>(&mut fp, |f, s| { + f.mant >>= s; + f.exp += s; + }); + assert_eq!(fp.mant, 0); + assert_eq!(fp.exp, 1); + + let mut fp = ExtendedFloat { + mant: 9223372036854776832, + exp: -10, + }; + rounding::round::<f64, _>(&mut fp, |f, s| { + f.mant >>= s; + f.exp += s; + // Round-up. + f.mant += 1; + }); + assert_eq!(fp.mant, 1); + assert_eq!(fp.exp, 1); + + // Round-down + let mut fp = ExtendedFloat { + mant: 9223372036854776832, + exp: -10, + }; + rounding::round::<f64, _>(&mut fp, |f, s| { + rounding::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| { + is_above || (is_odd && is_halfway) + }); + }); + assert_eq!(fp.mant, 0); + assert_eq!(fp.exp, 1); + + // Round up + let mut fp = ExtendedFloat { + mant: 9223372036854778880, + exp: -10, + }; + rounding::round::<f64, _>(&mut fp, |f, s| { + rounding::round_nearest_tie_even(f, s, |is_odd, is_halfway, is_above| { + is_above || (is_odd && is_halfway) + }); + }); + assert_eq!(fp.mant, 2); + assert_eq!(fp.exp, 1); + + // Round down + let mut fp = ExtendedFloat { + mant: 9223372036854778880, + exp: -10, + }; + rounding::round::<f64, _>(&mut fp, rounding::round_down); + assert_eq!(fp.mant, 1); + assert_eq!(fp.exp, 1); +} diff --git a/third_party/rust/minimal-lexical/tests/slow_tests.rs b/third_party/rust/minimal-lexical/tests/slow_tests.rs new file mode 100644 index 0000000000..2afea69e90 --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/slow_tests.rs @@ -0,0 +1,337 @@ +mod stackvec; + +use minimal_lexical::bigint::Bigint; +use minimal_lexical::extended_float::ExtendedFloat; +use minimal_lexical::num::Float; +use minimal_lexical::number::Number; +use minimal_lexical::slow; +use stackvec::vec_from_u32; + +fn b<F: Float>(float: F) -> (u64, i32) { + let fp = slow::b(float); + (fp.mant, fp.exp) +} + +fn bh<F: Float>(float: F) -> (u64, i32) { + let fp = slow::bh(float); + (fp.mant, fp.exp) +} + +#[test] +fn b_test() { + assert_eq!(b(1e-45_f32), (1, -149)); + assert_eq!(b(5e-324_f64), (1, -1074)); + assert_eq!(b(1e-323_f64), (2, -1074)); + assert_eq!(b(2e-323_f64), (4, -1074)); + assert_eq!(b(3e-323_f64), (6, -1074)); + assert_eq!(b(4e-323_f64), (8, -1074)); + assert_eq!(b(5e-323_f64), (10, -1074)); + assert_eq!(b(6e-323_f64), (12, -1074)); + assert_eq!(b(7e-323_f64), (14, -1074)); + assert_eq!(b(8e-323_f64), (16, -1074)); + assert_eq!(b(9e-323_f64), (18, -1074)); + assert_eq!(b(1_f32), (8388608, -23)); + assert_eq!(b(1_f64), (4503599627370496, -52)); + assert_eq!(b(1e38_f32), (9860761, 103)); + assert_eq!(b(1e308_f64), (5010420900022432, 971)); +} + +#[test] +fn bh_test() { + assert_eq!(bh(1e-45_f32), (3, -150)); + assert_eq!(bh(5e-324_f64), (3, -1075)); + assert_eq!(bh(1_f32), (16777217, -24)); + assert_eq!(bh(1_f64), (9007199254740993, -53)); + assert_eq!(bh(1e38_f32), (19721523, 102)); + assert_eq!(bh(1e308_f64), (10020841800044865, 970)); +} + +#[test] +fn slow_test() { + // 5e-324, round-down. + let integer = b"2"; + let fraction = b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999"; + let num = Number { + mantissa: 2470328229206232720, + exponent: -342, + many_digits: true, + }; + let fp = ExtendedFloat { + mant: 1 << 63, + exp: -63, + }; + let result = slow::slow::<f64, _, _>(num.clone(), fp, integer.iter(), fraction.iter()); + assert_eq!(result.mant, 0); + assert_eq!(result.exp, 0); + + // 5e-324, round-up. + let fraction = b"47032822920623272088284396434110686182529901307162382212792841250337753635104375932649918180817996189898282347722858865463328355177969898199387398005390939063150356595155702263922908583924491051844359318028499365361525003193704576782492193656236698636584807570015857692699037063119282795585513329278343384093519780155312465972635795746227664652728272200563740064854999770965994704540208281662262378573934507363390079677619305775067401763246736009689513405355374585166611342237666786041621596804619144672918403005300575308490487653917113865916462395249126236538818796362393732804238910186723484976682350898633885879256283027559956575244555072551893136908362547791869486679949683240497058210285131854513962138377228261454376934125320985913276672363281251"; + let result = slow::slow::<f64, _, _>(num.clone(), fp, integer.iter(), fraction.iter()); + assert_eq!(result.mant, 1); + assert_eq!(result.exp, 0); + + // 8.98846567431158e+307 + let integer = b"8"; + let fraction = b"9884656743115805365666807213050294962762414131308158973971342756154045415486693752413698006024096935349884403114202125541629105369684531108613657287705365884742938136589844238179474556051429647415148697857438797685859063890851407391008830874765563025951597582513936655578157348020066364210154316532161708032"; + let num = Number { + mantissa: 8988465674311580536, + exponent: 289, + many_digits: true, + }; + let fp = ExtendedFloat { + mant: 9223372036854776832, + exp: 2035, + }; + let result = slow::slow::<f64, _, _>(num.clone(), fp, integer.iter(), fraction.iter()); + assert_eq!(result.mant, 0); + assert_eq!(result.exp, 2046); + + // 8.988465674311582e+307 + let fraction = b"98846567431158053656668072130502949627624141313081589739713427561540454154866937524136980060240969353498844031142021255416291053696845311086136572877053658847429381365898442381794745560514296474151486978574387976858590638908514073910088308747655630259515975825139366555781573480200663642101543165321617080321"; + let result = slow::slow::<f64, _, _>(num.clone(), fp, integer.iter(), fraction.iter()); + assert_eq!(result.mant, 1); + assert_eq!(result.exp, 2046); +} + +#[test] +fn positive_digit_comp_test() { + // 8.98846567431158e+307 + let bigmant = Bigint { + data: vec_from_u32(&[ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1024, 2147483648, + ]), + }; + let exponent = 307 + 1 - 308; + let result = slow::positive_digit_comp::<f64>(bigmant, exponent); + assert_eq!(result.mant, 0); + assert_eq!(result.exp, 2046); + + // 8.988465674311582e+307 + let bigmant = Bigint { + data: vec_from_u32(&[ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1024, 2147483648, + ]), + }; + let exponent = 307 + 1 - 308; + let result = slow::positive_digit_comp::<f64>(bigmant, exponent); + assert_eq!(result.mant, 1); + assert_eq!(result.exp, 2046); +} + +#[test] +fn negative_digit_comp_test() { + // 5e-324, below halfway, round-down to 0.0. + let bigmant = Bigint { + data: vec_from_u32(&[ + 1727738439, 330069557, 3509095598, 686205316, 156923684, 750687444, 2688855918, + 28211928, 1887482096, 3222998811, 913348873, 1652282845, 1600735541, 1664240266, + 84454144, 1487769792, 1855966778, 2832488299, 507030148, 1410055467, 2513359584, + 3453963205, 779237894, 3456088326, 3671009895, 3094451696, 1250165638, 2682979794, + 357925323, 1713890438, 3271046672, 3485897285, 3934710962, 1813530592, 199705026, + 976390839, 2805488572, 2194288220, 2094065006, 2592523639, 3798974617, 586957244, + 1409218821, 3442050171, 3789534764, 1380190380, 2055222457, 3535299831, 429482276, + 389342206, 133558576, 721875297, 3013586570, 540178306, 2389746866, 2313334501, + 422440635, 1288499129, 864978311, 842263325, 3016323856, 2282442263, 1440906063, + 3931458696, 3511314276, 1884879882, 946366824, 4260548261, 1073379659, 1732329252, + 3828972211, 1915607049, 3665440937, 1844358779, 3735281178, 2646335050, 1457460927, + 2940016422, 1051, + ]), + }; + let fp = ExtendedFloat { + mant: 1 << 63, + exp: -63, + }; + let exponent = -324 + 1 - 755; + let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent); + assert_eq!(result.mant, 0); + assert_eq!(result.exp, 0); + + // 5e-324, halfway, round-down to 0.0. + let bigmant = Bigint { + data: vec_from_u32(&[ + 2084786877, 507136210, 2666388819, 3110242527, 3178432722, 541916566, 208847286, + 3092404665, 83491860, 2893735989, 3973758097, 2600107496, 147629623, 1754010897, + 4226332273, 2587058081, 942453804, 88731834, 1319061990, 173208747, 1982493283, + 3808794987, 3874839738, 1854586992, 3508364323, 2021729080, 1899625710, 2420749567, + 816401711, 3059730605, 1570934109, 3138812023, 1756281367, 3205859133, 2985201975, + 1014588672, 3799556578, 577719905, 4052248225, 3649019757, 398935965, 56421532, + 976366795, 1876047791, 3147705595, 4025764546, 1097271882, 1910500779, 2397021233, + 1340419138, 2753207595, 3067328524, 2210626776, 1280440432, 3940874757, 4172726578, + 1035509558, 1062145421, 1465448826, 2990139501, 1785427751, 2093931515, 4055890033, + 3388365687, 2245484242, 3609657408, 3527114516, 1013577862, 2389075196, 426934091, + 3237939346, 1071362463, 4070999470, 250952461, 2280067948, 1097862995, 2226250520, + 221983348, 1, + ]), + }; + let exponent = -324 + 1 - 752; + let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent); + assert_eq!(result.mant, 0); + assert_eq!(result.exp, 0); + + // 5e-324, above halfway, round-up to 5e-324. + let bigmant = Bigint { + data: vec_from_u32(&[ + 3667999587, 776394808, 894084415, 1037654204, 1719556155, 1124198371, 2088472861, + 859275578, 834918607, 3167556114, 1082875312, 231271193, 1476296236, 360239786, + 3608617070, 100777043, 834603454, 887318342, 305718012, 1732087473, 2645063646, + 3728211506, 93691724, 1366000745, 723904866, 3037421624, 1816387920, 2732659194, + 3869049819, 532534979, 2824439209, 1323349161, 382944493, 1993820262, 4082215981, + 1555952134, 3635827414, 1482231762, 1867776587, 2130459211, 3989359658, 564215320, + 1173733358, 1580608728, 1412284882, 1602939803, 2382784237, 1925138608, 2495375854, + 519289497, 1762272177, 608514174, 631431287, 4214469733, 754041908, 3072560125, + 1765160997, 2031519620, 1769586374, 4131591237, 674408332, 3759445970, 1904194670, + 3818885807, 980005947, 1736835717, 911406800, 1545844036, 2415915482, 4269340915, + 2314622388, 2123690045, 2055289038, 2509524619, 1325843000, 2388695363, 787668722, + 2219833485, 10, + ]), + }; + let exponent = -324 + 1 - 753; + let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent); + assert_eq!(result.mant, 1); + assert_eq!(result.exp, 0); + + // 1e-323, below halfway, round-down to 5e-324. + let bigmant = Bigint { + data: vec_from_u32(&[ + 888248023, 990208672, 1937352202, 2058615950, 470771052, 2252062332, 3771600458, + 84635785, 1367478992, 1079061842, 2740046621, 661881239, 507239328, 697753503, + 253362433, 168342080, 1272933039, 4202497602, 1521090445, 4230166401, 3245111456, + 1771955024, 2337713684, 1778330386, 2423095095, 693420498, 3750496916, 3753972086, + 1073775970, 846704018, 1223205425, 1867757265, 3214198296, 1145624482, 599115079, + 2929172517, 4121498420, 2287897365, 1987227723, 3482603622, 2806989260, 1760871734, + 4227656463, 1736215921, 2778669702, 4140571142, 1870700075, 2015964902, 1288446830, + 1168026618, 400675728, 2165625891, 450825118, 1620534920, 2874273302, 2645036208, + 1267321906, 3865497387, 2594934933, 2526789975, 459036976, 2552359495, 27750894, + 3204441497, 1944008238, 1359672352, 2839100473, 4191710191, 3220138979, 902020460, + 2896982042, 1451853853, 2406388220, 1238109043, 2615908943, 3644037856, 77415486, + 230114675, 3155, + ]), + }; + let fp = ExtendedFloat { + mant: 1 << 63, + exp: -62, + }; + let exponent = -324 + 1 - 755; + let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent); + assert_eq!(result.mant, 1); + assert_eq!(result.exp, 0); + + // 1e-323, halfway, round-up to 1e-323. + let bigmant = Bigint { + data: vec_from_u32(&[ + 1959393335, 1521408631, 3704199161, 740792990, 945363576, 1625749700, 626541858, + 687279403, 250475582, 91273375, 3331339701, 3505355194, 442888870, 967065395, + 4089062228, 3466206949, 2827361413, 266195502, 3957185970, 519626241, 1652512553, + 2836450370, 3034584624, 1268793682, 1935158378, 1770219946, 1403909835, 2967281406, + 2449205134, 589257223, 417835033, 826501478, 973876807, 1027642808, 365671335, + 3043766018, 2808735142, 1733159717, 3566810083, 2357124681, 1196807897, 169264596, + 2929100385, 1333176077, 853182194, 3487359048, 3291815648, 1436535041, 2896096404, + 4021257415, 3964655489, 612050981, 2336913034, 3841321297, 3232689679, 3928245144, + 3106528676, 3186436263, 101379182, 380483912, 1061315959, 1986827250, 3577735508, + 1575162471, 2441485432, 2239037633, 1991408958, 3040733588, 2872258292, 1280802274, + 1123883446, 3214087391, 3623063818, 752857385, 2545236548, 3293588986, 2383784264, + 665950045, 3, + ]), + }; + let exponent = -324 + 1 - 752; + let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent); + assert_eq!(result.mant, 2); + assert_eq!(result.exp, 0); + + // 1e-323, above halfway, round-up to 1e-323. + let bigmant = Bigint { + data: vec_from_u32(&[ + 2414064167, 2329184426, 2682253245, 3112962612, 863701169, 3372595114, 1970451287, + 2577826735, 2504755821, 912733750, 3248625938, 693813579, 133921412, 1080719359, + 2235916618, 302331131, 2503810362, 2661955026, 917154036, 901295123, 3640223643, + 2594699927, 281075174, 4098002235, 2171714598, 522330280, 1154196466, 3903010287, + 3017214866, 1597604939, 4178350331, 3970047484, 1148833479, 1686493490, 3656713352, + 372889108, 2317547651, 151727992, 1308362466, 2096410338, 3378144383, 1692645962, + 3521200074, 446858888, 4236854647, 513852113, 2853385416, 1480448529, 3191160267, + 1557868492, 991849235, 1825542523, 1894293861, 4053474607, 2262125726, 627745783, + 1000515697, 1799591565, 1013791827, 3804839120, 2023224998, 2688403318, 1417616716, + 2866722830, 2940017843, 915539855, 2734220401, 342564812, 2952779151, 4218088154, + 2648899870, 2076102840, 1870899819, 3233606562, 3977529001, 2871118793, 2363006167, + 2364533159, 31, + ]), + }; + let exponent = -324 + 1 - 753; + let result = slow::negative_digit_comp::<f64>(bigmant, fp, exponent); + assert_eq!(result.mant, 2); + assert_eq!(result.exp, 0); +} + +#[test] +fn parse_mantissa_test() { + let max_digits = f64::MAX_DIGITS; + + // Large number of digits. + let integer = b"2"; + let fraction = b"4703282292062327208828439643411068618252990130716238221279284125033775363510437593264991818081799618989828234772285886546332835517796989819938739800539093906315035659515570226392290858392449105184435931802849936536152500319370457678249219365623669863658480757001585769269903706311928279558551332927834338409351978015531246597263579574622766465272827220056374006485499977096599470454020828166226237857393450736339007967761930577506740176324673600968951340535537458516661134223766678604162159680461914467291840300530057530849048765391711386591646239524912623653881879636239373280423891018672348497668235089863388587925628302755995657524455507255189313690836254779186948667994968324049705821028513185451396213837722826145437693412532098591327667236328124999"; + let (bigmant, count) = slow::parse_mantissa(integer.iter(), fraction.iter(), max_digits); + let expected = vec_from_u32(&[ + 1727738439, 330069557, 3509095598, 686205316, 156923684, 750687444, 2688855918, 28211928, + 1887482096, 3222998811, 913348873, 1652282845, 1600735541, 1664240266, 84454144, + 1487769792, 1855966778, 2832488299, 507030148, 1410055467, 2513359584, 3453963205, + 779237894, 3456088326, 3671009895, 3094451696, 1250165638, 2682979794, 357925323, + 1713890438, 3271046672, 3485897285, 3934710962, 1813530592, 199705026, 976390839, + 2805488572, 2194288220, 2094065006, 2592523639, 3798974617, 586957244, 1409218821, + 3442050171, 3789534764, 1380190380, 2055222457, 3535299831, 429482276, 389342206, + 133558576, 721875297, 3013586570, 540178306, 2389746866, 2313334501, 422440635, 1288499129, + 864978311, 842263325, 3016323856, 2282442263, 1440906063, 3931458696, 3511314276, + 1884879882, 946366824, 4260548261, 1073379659, 1732329252, 3828972211, 1915607049, + 3665440937, 1844358779, 3735281178, 2646335050, 1457460927, 2940016422, 1051, + ]); + assert_eq!(&*bigmant.data, &*expected); + assert_eq!(count, 755); + + // Truncation. + let integer = b"7"; + let fraction = b"4109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375332669816033062329967789262837"; + let (bigmant, count) = slow::parse_mantissa(integer.iter(), fraction.iter(), max_digits); + let expected = vec_from_u32(&[ + 983641521, 2202462645, 4170685875, 1591772364, 529830014, 803977727, 126733331, 1695971390, + 4089590927, 1532849076, 2705586665, 4046282448, 4076195232, 3230469892, 3059053929, + 79035789, 744229654, 2026438108, 3570486781, 2818088662, 3485839733, 3653138023, + 2857937689, 602717004, 3689362390, 283607819, 1783392475, 2053068939, 1888214698, + 550023429, 296880187, 1046779059, 1285361259, 84614934, 1627922685, 2023868765, 1987523901, + 743493573, 3897769089, 2210613570, 2261081349, 3015057659, 3949711644, 3346092916, + 2433639051, 36411806, 1050442, 269209477, 2649742673, 1494221829, 2763524503, 2514491481, + 2325312415, 1741242814, 2479923579, 1098250122, 2416211509, 3612906464, 403420662, + 3663250314, 1993722098, 365907183, 4270226312, 3962131185, 432952495, 2963635838, + 2996289227, 3200289391, 2753231690, 2780286109, 884373163, 1418533204, 3382415762, + 499541562, 3369625401, 3421327641, 3526770155, 3109983188, 1157439767, 734593155, + ]); + assert_eq!(&*bigmant.data, &*expected); + assert_eq!(count, max_digits + 1); + + // No fraction digits. + let integer = b"74109846876186981626485318930233205854758970392148714663837852375101326090531312779794975454245398856969484704316857659638998506553390969459816219401617281718945106978546710679176872575177347315553307795408549809608457500958111373034747658096871009590975442271004757307809711118935784838675653998783503015228055934046593739791790738723868299395818481660169122019456499931289798411362062484498678713572180352209017023903285791732520220528974020802906854021606612375549983402671300035812486479041385743401875520901590172592547146296175134159774938718574737870961645638908718119841271673056017045493004705269590165763776884908267986972573366521765567941072508764337560846003984904972149117463085539556354188641513168478436313080237596295773983001708984375332669816033062329967789262837"; + let fraction = b""; + let (bigmant, count) = slow::parse_mantissa(integer.iter(), fraction.iter(), max_digits); + assert_eq!(&*bigmant.data, &*expected); + assert_eq!(count, max_digits + 1); + + // Multiple of step (check we add our temporary correctly). + let integer = b"7410984687618698162648531893023320585475897039214871466383785237510132609053131277979497545424539885696948470431685765963899850655339096945981621940161728171894510697854671067917687257517734731555330779540854980960845750095811137303474765809687100959097544227100475730780971111893578483867565399878350301522805593404659373979179073872386829939581848166016912201945649993128979841136206248449867871357218035220901702390328579173252022052897402080290685402160661237554998340267130003581248647904138574340187552090159017259254714629617513415977493871857473787096164563890871811984127167305601704549300470526959016576377688490826798697257336652176556794107250876433756084600398490497214911746308553955635418864151316847843631308023759629577398300170898437533266981"; + let fraction = b""; + let (bigmant, count) = slow::parse_mantissa(integer.iter(), fraction.iter(), max_digits); + let expected = vec_from_u32(&[ + 617018405, 396211401, 2130402383, 3812547827, 4263683770, 3918012496, 1787721490, + 2493014694, 435464626, 3720854431, 2928509507, 2677932436, 369049650, 3606588290, + 231237141, 2231172875, 3358152367, 95217925, 2777810007, 1016185079, 596681915, 2331711780, + 593487272, 4212730845, 339602972, 4097829793, 262427536, 4182115035, 3414687403, + 3711518952, 4168896929, 483727327, 1657080031, 2785588628, 1009114769, 482126749, + 485376744, 1123705337, 3225501941, 2939050108, 1338451005, 2104263947, 3425461126, + 1834224928, 4061025704, 792093815, 2707019125, 3610271203, 4254101529, 1026215278, + 4117890107, 1748110416, 2535111606, 80965120, 3823822115, 2354910057, 590658512, + 2682089507, 159300272, 1776569442, 3382166479, 3222978591, 540586210, 934713382, + 2014123057, 1455555790, 4119131465, 3685912982, 3019947291, 3437891678, 2660105801, + 2605860762, 394373515, 4177081532, 1616198650, 1580399082, 2017617452, 3327697130, + 315505357, + ]); + assert_eq!(&*bigmant.data, &*expected); + assert_eq!(count, 760); +} diff --git a/third_party/rust/minimal-lexical/tests/stackvec.rs b/third_party/rust/minimal-lexical/tests/stackvec.rs new file mode 100644 index 0000000000..d5587f23f8 --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/stackvec.rs @@ -0,0 +1,32 @@ +use minimal_lexical::bigint; +#[cfg(feature = "alloc")] +pub use minimal_lexical::heapvec::HeapVec as VecType; +#[cfg(not(feature = "alloc"))] +pub use minimal_lexical::stackvec::StackVec as VecType; + +pub fn vec_from_u32(x: &[u32]) -> VecType { + let mut vec = VecType::new(); + #[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] + { + for &xi in x { + vec.try_push(xi as bigint::Limb).unwrap(); + } + } + + #[cfg(all(target_pointer_width = "64", not(target_arch = "sparc")))] + { + for xi in x.chunks(2) { + match xi.len() { + 1 => vec.try_push(xi[0] as bigint::Limb).unwrap(), + 2 => { + let xi0 = xi[0] as bigint::Limb; + let xi1 = xi[1] as bigint::Limb; + vec.try_push((xi1 << 32) | xi0).unwrap() + }, + _ => unreachable!(), + } + } + } + + vec +} diff --git a/third_party/rust/minimal-lexical/tests/vec_tests.rs b/third_party/rust/minimal-lexical/tests/vec_tests.rs new file mode 100644 index 0000000000..3a5f5886ad --- /dev/null +++ b/third_party/rust/minimal-lexical/tests/vec_tests.rs @@ -0,0 +1,395 @@ +mod stackvec; + +use core::cmp; +use minimal_lexical::bigint; +use stackvec::{vec_from_u32, VecType}; + +// u64::MAX and Limb::MAX for older Rustc versions. +const U64_MAX: u64 = 0xffff_ffff_ffff_ffff; +// LIMB_MAX +#[cfg(all(target_pointer_width = "64", not(target_arch = "sparc")))] +const LIMB_MAX: u64 = U64_MAX; +#[cfg(not(all(target_pointer_width = "64", not(target_arch = "sparc"))))] +const LIMB_MAX: u32 = 0xffff_ffff; + +#[test] +fn simple_test() { + // Test the simple properties of the stack vector. + let mut x = VecType::from_u64(1); + assert_eq!(x.len(), 1); + assert_eq!(x.is_empty(), false); + assert_eq!(x.capacity(), bigint::BIGINT_LIMBS); + x.try_push(5).unwrap(); + assert_eq!(x.len(), 2); + assert_eq!(x.pop(), Some(5)); + assert_eq!(x.len(), 1); + assert_eq!(&*x, &[1]); + x.try_extend(&[2, 3, 4]).unwrap(); + assert_eq!(x.len(), 4); + assert_eq!(&*x, &[1, 2, 3, 4]); + x.try_resize(6, 0).unwrap(); + assert_eq!(x.len(), 6); + assert_eq!(&*x, &[1, 2, 3, 4, 0, 0]); + x.try_resize(0, 0).unwrap(); + assert_eq!(x.len(), 0); + assert_eq!(x.is_empty(), true); + + let x = VecType::try_from(&[5, 1]).unwrap(); + assert_eq!(x.len(), 2); + assert_eq!(x.is_empty(), false); + if bigint::LIMB_BITS == 32 { + assert_eq!(x.hi64(), (0x8000000280000000, false)); + } else { + assert_eq!(x.hi64(), (0x8000000000000002, true)); + } + let rview = bigint::rview(&x); + assert_eq!(x[0], 5); + assert_eq!(x[1], 1); + assert_eq!(rview[0], 1); + assert_eq!(rview[1], 5); + assert_eq!(x.len(), 2); + + assert_eq!(VecType::from_u64(U64_MAX).hi64(), (U64_MAX, false)); +} + +#[test] +fn hi64_test() { + assert_eq!(VecType::from_u64(0xA).hi64(), (0xA000000000000000, false)); + assert_eq!(VecType::from_u64(0xAB).hi64(), (0xAB00000000000000, false)); + assert_eq!(VecType::from_u64(0xAB00000000).hi64(), (0xAB00000000000000, false)); + assert_eq!(VecType::from_u64(0xA23456789A).hi64(), (0xA23456789A000000, false)); +} + +#[test] +fn cmp_test() { + // Simple + let x = VecType::from_u64(1); + let y = VecType::from_u64(2); + assert_eq!(x.partial_cmp(&x), Some(cmp::Ordering::Equal)); + assert_eq!(x.cmp(&x), cmp::Ordering::Equal); + assert_eq!(x.cmp(&y), cmp::Ordering::Less); + + // Check asymmetric + let x = VecType::try_from(&[5, 1]).unwrap(); + let y = VecType::from_u64(2); + assert_eq!(x.cmp(&x), cmp::Ordering::Equal); + assert_eq!(x.cmp(&y), cmp::Ordering::Greater); + + // Check when we use reverse ordering properly. + let x = VecType::try_from(&[5, 1, 9]).unwrap(); + let y = VecType::try_from(&[6, 2, 8]).unwrap(); + assert_eq!(x.cmp(&x), cmp::Ordering::Equal); + assert_eq!(x.cmp(&y), cmp::Ordering::Greater); + + // Complex scenario, check it properly uses reverse ordering. + let x = VecType::try_from(&[0, 1, 9]).unwrap(); + let y = VecType::try_from(&[4294967295, 0, 9]).unwrap(); + assert_eq!(x.cmp(&x), cmp::Ordering::Equal); + assert_eq!(x.cmp(&y), cmp::Ordering::Greater); +} + +#[test] +fn math_test() { + let mut x = VecType::try_from(&[0, 1, 9]).unwrap(); + assert_eq!(x.is_normalized(), true); + x.try_push(0).unwrap(); + assert_eq!(&*x, &[0, 1, 9, 0]); + assert_eq!(x.is_normalized(), false); + x.normalize(); + assert_eq!(&*x, &[0, 1, 9]); + assert_eq!(x.is_normalized(), true); + + x.add_small(1); + assert_eq!(&*x, &[1, 1, 9]); + x.add_small(LIMB_MAX); + assert_eq!(&*x, &[0, 2, 9]); + + x.mul_small(3); + assert_eq!(&*x, &[0, 6, 27]); + x.mul_small(LIMB_MAX); + let expected: VecType = if bigint::LIMB_BITS == 32 { + vec_from_u32(&[0, 4294967290, 4294967274, 26]) + } else { + vec_from_u32(&[0, 0, 4294967290, 4294967295, 4294967274, 4294967295, 26]) + }; + assert_eq!(&*x, &*expected); + + let mut x = VecType::from_u64(0xFFFFFFFF); + let y = VecType::from_u64(5); + x *= &y; + let expected: VecType = vec_from_u32(&[0xFFFFFFFB, 0x4]); + assert_eq!(&*x, &*expected); + + // Test with carry + let mut x = VecType::from_u64(1); + assert_eq!(&*x, &[1]); + x.add_small(LIMB_MAX); + assert_eq!(&*x, &[0, 1]); +} + +#[test] +fn scalar_add_test() { + assert_eq!(bigint::scalar_add(5, 5), (10, false)); + assert_eq!(bigint::scalar_add(LIMB_MAX, 1), (0, true)); +} + +#[test] +fn scalar_mul_test() { + assert_eq!(bigint::scalar_mul(5, 5, 0), (25, 0)); + assert_eq!(bigint::scalar_mul(5, 5, 1), (26, 0)); + assert_eq!(bigint::scalar_mul(LIMB_MAX, 2, 0), (LIMB_MAX - 1, 1)); +} + +#[test] +fn small_add_test() { + let mut x = VecType::from_u64(4294967295); + bigint::small_add(&mut x, 5); + let expected: VecType = vec_from_u32(&[4, 1]); + assert_eq!(&*x, &*expected); + + let mut x = VecType::from_u64(5); + bigint::small_add(&mut x, 7); + let expected = VecType::from_u64(12); + assert_eq!(&*x, &*expected); + + // Single carry, internal overflow + let mut x = VecType::from_u64(0x80000000FFFFFFFF); + bigint::small_add(&mut x, 7); + let expected: VecType = vec_from_u32(&[6, 0x80000001]); + assert_eq!(&*x, &*expected); + + // Double carry, overflow + let mut x = VecType::from_u64(0xFFFFFFFFFFFFFFFF); + bigint::small_add(&mut x, 7); + let expected: VecType = vec_from_u32(&[6, 0, 1]); + assert_eq!(&*x, &*expected); +} + +#[test] +fn small_mul_test() { + // No overflow check, 1-int. + let mut x = VecType::from_u64(5); + bigint::small_mul(&mut x, 7); + let expected = VecType::from_u64(35); + assert_eq!(&*x, &*expected); + + // No overflow check, 2-ints. + let mut x = VecType::from_u64(0x4000000040000); + bigint::small_mul(&mut x, 5); + let expected: VecType = vec_from_u32(&[0x00140000, 0x140000]); + assert_eq!(&*x, &*expected); + + // Overflow, 1 carry. + let mut x = VecType::from_u64(0x33333334); + bigint::small_mul(&mut x, 5); + let expected: VecType = vec_from_u32(&[4, 1]); + assert_eq!(&*x, &*expected); + + // Overflow, 1 carry, internal. + let mut x = VecType::from_u64(0x133333334); + bigint::small_mul(&mut x, 5); + let expected: VecType = vec_from_u32(&[4, 6]); + assert_eq!(&*x, &*expected); + + // Overflow, 2 carries. + let mut x = VecType::from_u64(0x3333333333333334); + bigint::small_mul(&mut x, 5); + let expected: VecType = vec_from_u32(&[4, 0, 1]); + assert_eq!(&*x, &*expected); +} + +#[test] +fn pow_test() { + let mut x = VecType::from_u64(1); + bigint::pow(&mut x, 2); + let expected = VecType::from_u64(25); + assert_eq!(&*x, &*expected); + + let mut x = VecType::from_u64(1); + bigint::pow(&mut x, 15); + let expected: VecType = vec_from_u32(&[452807053, 7]); + assert_eq!(&*x, &*expected); + + let mut x = VecType::from_u64(1); + bigint::pow(&mut x, 16); + let expected: VecType = vec_from_u32(&[2264035265, 35]); + assert_eq!(&*x, &*expected); + + let mut x = VecType::from_u64(1); + bigint::pow(&mut x, 17); + let expected: VecType = vec_from_u32(&[2730241733, 177]); + assert_eq!(&*x, &*expected); + + let mut x = VecType::from_u64(1); + bigint::pow(&mut x, 302); + let expected: VecType = vec_from_u32(&[ + 2443090281, 2149694430, 2297493928, 1584384001, 1279504719, 1930002239, 3312868939, + 3735173465, 3523274756, 2025818732, 1641675015, 2431239749, 4292780461, 3719612855, + 4174476133, 3296847770, 2677357556, 638848153, 2198928114, 3285049351, 2159526706, + 626302612, + ]); + assert_eq!(&*x, &*expected); +} + +#[test] +fn large_add_test() { + // Overflow, both single values + let mut x = VecType::from_u64(4294967295); + let y = VecType::from_u64(5); + bigint::large_add(&mut x, &y); + let expected: VecType = vec_from_u32(&[4, 1]); + assert_eq!(&*x, &*expected); + + // No overflow, single value + let mut x = VecType::from_u64(5); + let y = VecType::from_u64(7); + bigint::large_add(&mut x, &y); + let expected = VecType::from_u64(12); + assert_eq!(&*x, &*expected); + + // Single carry, internal overflow + let mut x = VecType::from_u64(0x80000000FFFFFFFF); + let y = VecType::from_u64(7); + bigint::large_add(&mut x, &y); + let expected: VecType = vec_from_u32(&[6, 0x80000001]); + assert_eq!(&*x, &*expected); + + // 1st overflows, 2nd doesn't. + let mut x = VecType::from_u64(0x7FFFFFFFFFFFFFFF); + let y = VecType::from_u64(0x7FFFFFFFFFFFFFFF); + bigint::large_add(&mut x, &y); + let expected: VecType = vec_from_u32(&[0xFFFFFFFE, 0xFFFFFFFF]); + assert_eq!(&*x, &*expected); + + // Both overflow. + let mut x = VecType::from_u64(0x8FFFFFFFFFFFFFFF); + let y = VecType::from_u64(0x7FFFFFFFFFFFFFFF); + bigint::large_add(&mut x, &y); + let expected: VecType = vec_from_u32(&[0xFFFFFFFE, 0x0FFFFFFF, 1]); + assert_eq!(&*x, &*expected); +} + +#[test] +fn large_mul_test() { + // Test by empty + let mut x = VecType::from_u64(0xFFFFFFFF); + let y = VecType::new(); + bigint::large_mul(&mut x, &y); + let expected = VecType::new(); + assert_eq!(&*x, &*expected); + + // Simple case + let mut x = VecType::from_u64(0xFFFFFFFF); + let y = VecType::from_u64(5); + bigint::large_mul(&mut x, &y); + let expected: VecType = vec_from_u32(&[0xFFFFFFFB, 0x4]); + assert_eq!(&*x, &*expected); + + // Large u32, but still just as easy. + let mut x = VecType::from_u64(0xFFFFFFFF); + let y = VecType::from_u64(0xFFFFFFFE); + bigint::large_mul(&mut x, &y); + let expected: VecType = vec_from_u32(&[0x2, 0xFFFFFFFD]); + assert_eq!(&*x, &*expected); + + // Let's multiply two large values together. + let mut x: VecType = vec_from_u32(&[0xFFFFFFFE, 0x0FFFFFFF, 1]); + let y: VecType = vec_from_u32(&[0x99999999, 0x99999999, 0xCCCD9999, 0xCCCC]); + bigint::large_mul(&mut x, &y); + let expected: VecType = + vec_from_u32(&[0xCCCCCCCE, 0x5CCCCCCC, 0x9997FFFF, 0x33319999, 0x999A7333, 0xD999]); + assert_eq!(&*x, &*expected); +} + +#[test] +fn very_large_mul_test() { + // Test cases triggered to that would normally use `karatsuba_mul`. + // Karatsuba multiplication was ripped out, however, these are useful + // test cases. + let mut x: VecType = vec_from_u32(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + let y: VecType = vec_from_u32(&[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); + bigint::large_mul(&mut x, &y); + let expected: VecType = vec_from_u32(&[ + 4, 13, 28, 50, 80, 119, 168, 228, 300, 385, 484, 598, 728, 875, 1040, 1224, 1340, 1435, + 1508, 1558, 1584, 1585, 1560, 1508, 1428, 1319, 1180, 1010, 808, 573, 304, + ]); + assert_eq!(&*x, &*expected); + + // Test cases triggered to that would normally use `karatsuba_uneven_mul`. + let mut x: VecType = vec_from_u32(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + let y: VecType = vec_from_u32(&[ + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + ]); + bigint::large_mul(&mut x, &y); + let expected: VecType = vec_from_u32(&[ + 4, 13, 28, 50, 80, 119, 168, 228, 300, 385, 484, 598, 728, 875, 1040, 1224, 1360, 1496, + 1632, 1768, 1904, 2040, 2176, 2312, 2448, 2584, 2720, 2856, 2992, 3128, 3264, 3400, 3536, + 3672, 3770, 3829, 3848, 3826, 3762, 3655, 3504, 3308, 3066, 2777, 2440, 2054, 1618, 1131, + 592, + ]); + assert_eq!(&*x, &*expected); +} + +#[test] +fn bit_length_test() { + let x: VecType = vec_from_u32(&[0, 0, 0, 1]); + assert_eq!(bigint::bit_length(&x), 97); + + let x: VecType = vec_from_u32(&[0, 0, 0, 3]); + assert_eq!(bigint::bit_length(&x), 98); + + let x = VecType::from_u64(1 << 31); + assert_eq!(bigint::bit_length(&x), 32); +} + +#[test] +fn shl_bits_test() { + let mut x = VecType::from_u64(0xD2210408); + bigint::shl_bits(&mut x, 5); + let expected: VecType = vec_from_u32(&[0x44208100, 0x1A]); + assert_eq!(&*x, &*expected); +} + +#[test] +fn shl_limbs_test() { + let mut x = VecType::from_u64(0xD2210408); + bigint::shl_limbs(&mut x, 2); + let expected: VecType = if bigint::LIMB_BITS == 32 { + vec_from_u32(&[0, 0, 0xD2210408]) + } else { + vec_from_u32(&[0, 0, 0, 0, 0xD2210408]) + }; + assert_eq!(&*x, &*expected); +} + +#[test] +fn shl_test() { + // Pattern generated via `''.join(["1" +"0"*i for i in range(20)])` + let mut x = VecType::from_u64(0xD2210408); + bigint::shl(&mut x, 5); + let expected: VecType = vec_from_u32(&[0x44208100, 0x1A]); + assert_eq!(&*x, &*expected); + + bigint::shl(&mut x, 32); + let expected: VecType = vec_from_u32(&[0, 0x44208100, 0x1A]); + assert_eq!(&*x, &*expected); + + bigint::shl(&mut x, 27); + let expected: VecType = vec_from_u32(&[0, 0, 0xD2210408]); + assert_eq!(&*x, &*expected); + + // 96-bits of previous pattern + let mut x: VecType = vec_from_u32(&[0x20020010, 0x8040100, 0xD2210408]); + bigint::shl(&mut x, 5); + let expected: VecType = vec_from_u32(&[0x400200, 0x802004, 0x44208101, 0x1A]); + assert_eq!(&*x, &*expected); + + bigint::shl(&mut x, 32); + let expected: VecType = vec_from_u32(&[0, 0x400200, 0x802004, 0x44208101, 0x1A]); + assert_eq!(&*x, &*expected); + + bigint::shl(&mut x, 27); + let expected: VecType = vec_from_u32(&[0, 0, 0x20020010, 0x8040100, 0xD2210408]); + assert_eq!(&*x, &*expected); +} |