mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
libm: Add tests against rug for i256
This commit is contained in:
@@ -5,11 +5,11 @@
|
||||
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use libm::support::{HInt, u256};
|
||||
use libm::support::{HInt, i256, u256};
|
||||
type BigInt = rug::Integer;
|
||||
|
||||
use libm_test::bigint_fuzz_iteration_count;
|
||||
use libm_test::generate::random::SEED;
|
||||
use libm_test::{MinInt, bigint_fuzz_iteration_count};
|
||||
use rand::{RngExt, SeedableRng};
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use rug::Assign;
|
||||
@@ -25,27 +25,56 @@ fn random_u256(rng: &mut ChaCha8Rng) -> u256 {
|
||||
u256 { lo, hi }
|
||||
}
|
||||
|
||||
fn assign_bigint(bx: &mut BigInt, x: u256) {
|
||||
bx.assign_digits(&[x.lo, x.hi], Order::Lsf);
|
||||
fn random_i256(rng: &mut ChaCha8Rng) -> i256 {
|
||||
random_u256(rng).signed()
|
||||
}
|
||||
|
||||
fn from_bigint(bx: &mut BigInt) -> u256 {
|
||||
fn assign_bigint_u256(bx: &mut BigInt, x: u256) {
|
||||
bx.assign(x.hi);
|
||||
*bx <<= 128;
|
||||
*bx += x.lo;
|
||||
}
|
||||
|
||||
fn assign_bigint_i256(bx: &mut BigInt, x: i256) {
|
||||
bx.assign(x.hi);
|
||||
*bx <<= 128;
|
||||
*bx += x.lo;
|
||||
}
|
||||
|
||||
/// Note that this destroys the result in `bx`.
|
||||
fn from_bigint_u256(bx: &mut BigInt) -> u256 {
|
||||
// Truncate so the result fits into `[u128; 2]`. This makes all ops overflowing.
|
||||
*bx &= &*BIGINT_U256_MAX;
|
||||
let mut bres = [0u128, 0];
|
||||
bx.write_digits(&mut bres, Order::Lsf);
|
||||
bx.assign(0);
|
||||
bx.assign(0); // prevent accidental reuse
|
||||
u256 {
|
||||
lo: bres[0],
|
||||
hi: bres[1],
|
||||
}
|
||||
}
|
||||
|
||||
fn check_one(msg: impl Fn() -> String, actual: u256, expected: &mut BigInt) {
|
||||
let expected = from_bigint(expected);
|
||||
/// Note that this destroys the result in `bx`.
|
||||
fn from_bigint_i256(bx: &mut BigInt) -> i256 {
|
||||
// Truncate so the result fits into `[u128; 2]`. This makes all ops overflowing.
|
||||
*bx &= &*BIGINT_U256_MAX;
|
||||
let lo = bx.to_u128_wrapping();
|
||||
*bx >>= 128;
|
||||
let hi = bx.to_i128_wrapping();
|
||||
bx.assign(0); // prevent accidental reuse
|
||||
i256 { hi, lo }
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_same_u256(msg: impl Fn() -> String, actual: u256, expected_big: &mut BigInt) {
|
||||
let expected = from_bigint_u256(expected_big);
|
||||
if actual != expected {
|
||||
let mut act_big = BigInt::new();
|
||||
assign_bigint_u256(&mut act_big, actual);
|
||||
panic!(
|
||||
"Test failure: {}\n\
|
||||
actual: {act_big}\n\
|
||||
expected: {expected_big}\n\
|
||||
actual: {actual:#x}\n\
|
||||
expected: {expected:#x}\
|
||||
",
|
||||
@@ -54,6 +83,98 @@ fn check_one(msg: impl Fn() -> String, actual: u256, expected: &mut BigInt) {
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_same_i256(msg: impl Fn() -> String, actual: i256, expected_big: &mut BigInt) {
|
||||
let expected = from_bigint_i256(expected_big);
|
||||
if actual != expected {
|
||||
let mut act_big = BigInt::new();
|
||||
assign_bigint_i256(&mut act_big, actual);
|
||||
panic!(
|
||||
"Test failure: {}\n\
|
||||
actual: {act_big}\n\
|
||||
expected: {expected_big}\n\
|
||||
actual: {actual:#x}\n\
|
||||
expected: {expected:#x}\
|
||||
",
|
||||
msg()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify the test setup.
|
||||
#[test]
|
||||
fn mp_u256_roundtrip() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_u256(&mut rng);
|
||||
assign_bigint_u256(&mut bx, x);
|
||||
assert_eq!(from_bigint_u256(&mut bx), x);
|
||||
}
|
||||
|
||||
// Check wraparound
|
||||
assign_bigint_u256(&mut bx, u256::MAX);
|
||||
bx += 1;
|
||||
assert_eq!(from_bigint_u256(&mut bx), u256::MIN);
|
||||
assign_bigint_u256(&mut bx, u256::MIN);
|
||||
bx -= 1;
|
||||
assert_eq!(from_bigint_u256(&mut bx), u256::MAX);
|
||||
}
|
||||
|
||||
/// Verify the test setup.
|
||||
#[test]
|
||||
fn mp_i256_roundtrip() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_i256(&mut rng);
|
||||
assign_bigint_i256(&mut bx, x);
|
||||
assert_eq!(from_bigint_i256(&mut bx), x);
|
||||
}
|
||||
|
||||
// Check wraparound
|
||||
assign_bigint_i256(&mut bx, i256::MAX);
|
||||
bx += 1;
|
||||
assert_eq!(from_bigint_i256(&mut bx), i256::MIN);
|
||||
assign_bigint_i256(&mut bx, i256::MIN);
|
||||
bx -= 1;
|
||||
assert_eq!(from_bigint_i256(&mut bx), i256::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_u256_ord() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
let mut by = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_u256(&mut rng);
|
||||
let y = random_u256(&mut rng);
|
||||
assign_bigint_u256(&mut bx, x);
|
||||
assign_bigint_u256(&mut by, y);
|
||||
|
||||
assert_eq!(x.cmp(&y), bx.cmp(&by), "cmp({x:#x}, {y:#x})");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_i256_ord() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
let mut by = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_i256(&mut rng);
|
||||
let y = random_i256(&mut rng);
|
||||
assign_bigint_i256(&mut bx, x);
|
||||
assign_bigint_i256(&mut by, y);
|
||||
|
||||
assert_eq!(x.cmp(&y), bx.cmp(&by), "cmp({x:#x}, {y:#x})");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_u256_bitor() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
@@ -63,11 +184,28 @@ fn mp_u256_bitor() {
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_u256(&mut rng);
|
||||
let y = random_u256(&mut rng);
|
||||
assign_bigint(&mut bx, x);
|
||||
assign_bigint(&mut by, y);
|
||||
assign_bigint_u256(&mut bx, x);
|
||||
assign_bigint_u256(&mut by, y);
|
||||
let actual = x | y;
|
||||
bx |= &by;
|
||||
check_one(|| format!("{x:#x} ^ {y:#x}"), actual, &mut bx);
|
||||
assert_same_u256(|| format!("{x:#x} ^ {y:#x}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_i256_bitor() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
let mut by = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_i256(&mut rng);
|
||||
let y = random_i256(&mut rng);
|
||||
assign_bigint_i256(&mut bx, x);
|
||||
assign_bigint_i256(&mut by, y);
|
||||
let actual = x | y;
|
||||
bx |= &by;
|
||||
assert_same_i256(|| format!("{x:#x} ^ {y:#x}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,10 +216,24 @@ fn mp_u256_not() {
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_u256(&mut rng);
|
||||
assign_bigint(&mut bx, x);
|
||||
assign_bigint_u256(&mut bx, x);
|
||||
let actual = !x;
|
||||
bx.not_assign();
|
||||
check_one(|| format!("!{x:#x}"), actual, &mut bx);
|
||||
assert_same_u256(|| format!("!{x:#x}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_i256_not() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_i256(&mut rng);
|
||||
assign_bigint_i256(&mut bx, x);
|
||||
let actual = !x;
|
||||
bx.not_assign();
|
||||
assert_same_i256(|| format!("!{x:#x}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,8 +246,9 @@ fn mp_u256_add() {
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_u256(&mut rng);
|
||||
let y = random_u256(&mut rng);
|
||||
assign_bigint(&mut bx, x);
|
||||
assign_bigint(&mut by, y);
|
||||
assign_bigint_u256(&mut bx, x);
|
||||
assign_bigint_u256(&mut by, y);
|
||||
// Emulate wrapping semantics with panicking ops
|
||||
let actual = if u256::MAX - x >= y {
|
||||
x + y
|
||||
} else {
|
||||
@@ -104,7 +257,35 @@ fn mp_u256_add() {
|
||||
y - (u256::MAX - x) - 1_u128.widen()
|
||||
};
|
||||
bx += &by;
|
||||
check_one(|| format!("{x:#x} + {y:#x}"), actual, &mut bx);
|
||||
assert_same_u256(|| format!("{x:#x} + {y:#x}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_i256_add() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
let mut by = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_i256(&mut rng);
|
||||
let y = random_i256(&mut rng);
|
||||
assign_bigint_i256(&mut bx, x);
|
||||
assign_bigint_i256(&mut by, y);
|
||||
|
||||
// Emulate wrapping semantics with panicking ops
|
||||
let actual = if x > i256::ZERO && y > i256::MAX - x {
|
||||
// Overflow condition
|
||||
(x + i256::MIN) + (y + i256::MIN)
|
||||
} else if x < i256::ZERO && y < i256::MIN - x {
|
||||
// Underflow condition
|
||||
(x - i256::MIN) + (y - i256::MIN)
|
||||
} else {
|
||||
// Otherwise there is no overflow
|
||||
x + y
|
||||
};
|
||||
bx += &by;
|
||||
assert_same_i256(|| format!("{x:#x} + {y:#x}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,15 +298,41 @@ fn mp_u256_sub() {
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_u256(&mut rng);
|
||||
let y = random_u256(&mut rng);
|
||||
assign_bigint(&mut bx, x);
|
||||
assign_bigint(&mut by, y);
|
||||
assign_bigint_u256(&mut bx, x);
|
||||
assign_bigint_u256(&mut by, y);
|
||||
|
||||
// since the operators (may) panic on overflow,
|
||||
// we should test something that doesn't
|
||||
let actual = if x >= y { x - y } else { y - x };
|
||||
bx -= &by;
|
||||
bx.abs_mut();
|
||||
check_one(|| format!("{x:#x} - {y:#x}"), actual, &mut bx);
|
||||
assert_same_u256(|| format!("{x:#x} - {y:#x}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_i256_sub() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
let mut by = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_i256(&mut rng);
|
||||
let y = random_i256(&mut rng);
|
||||
assign_bigint_i256(&mut bx, x);
|
||||
assign_bigint_i256(&mut by, y);
|
||||
dbg!(&bx, &by);
|
||||
|
||||
// Emulate wrapping semantics with panicking ops
|
||||
let actual = if y > i256::ZERO && x < i256::MIN + y {
|
||||
(x - i256::MIN) - (y + i256::MIN)
|
||||
} else if y < i256::ZERO && x > i256::MAX + y {
|
||||
(x + i256::MIN) - (y - i256::MIN)
|
||||
} else {
|
||||
x - y
|
||||
};
|
||||
bx -= &by;
|
||||
assert_same_i256(|| format!("{x:#x} - {y:#x}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +344,25 @@ fn mp_u256_shl() {
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_u256(&mut rng);
|
||||
let shift: u32 = rng.random_range(0..256);
|
||||
assign_bigint(&mut bx, x);
|
||||
assign_bigint_u256(&mut bx, x);
|
||||
let actual = x << shift;
|
||||
bx <<= shift;
|
||||
check_one(|| format!("{x:#x} << {shift}"), actual, &mut bx);
|
||||
assert_same_u256(|| format!("{x:#x} << {shift}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_i256_shl() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_i256(&mut rng);
|
||||
let shift: u32 = rng.random_range(0..256);
|
||||
assign_bigint_i256(&mut bx, x);
|
||||
let actual = x << shift;
|
||||
bx <<= shift;
|
||||
assert_same_i256(|| format!("{x:#x} << {shift}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,10 +374,25 @@ fn mp_u256_shr() {
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_u256(&mut rng);
|
||||
let shift: u32 = rng.random_range(0..256);
|
||||
assign_bigint(&mut bx, x);
|
||||
assign_bigint_u256(&mut bx, x);
|
||||
let actual = x >> shift;
|
||||
bx >>= shift;
|
||||
check_one(|| format!("{x:#x} >> {shift}"), actual, &mut bx);
|
||||
assert_same_u256(|| format!("{x:#x} >> {shift}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp_i256_shr() {
|
||||
let mut rng = ChaCha8Rng::from_seed(*SEED);
|
||||
let mut bx = BigInt::new();
|
||||
|
||||
for _ in 0..bigint_fuzz_iteration_count() {
|
||||
let x = random_i256(&mut rng);
|
||||
let shift: u32 = rng.random_range(0..256);
|
||||
assign_bigint_i256(&mut bx, x);
|
||||
let actual = x >> shift;
|
||||
bx >>= shift;
|
||||
assert_same_i256(|| format!("{x:#x} >> {shift}"), actual, &mut bx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +409,7 @@ fn mp_u256_u128_widen_mul() {
|
||||
by.assign(y);
|
||||
let actual = x.widen_mul(y);
|
||||
bx *= &by;
|
||||
check_one(
|
||||
assert_same_u256(
|
||||
|| format!("{x:#034x}.widen_mul({y:#034x})"),
|
||||
actual,
|
||||
&mut bx,
|
||||
|
||||
@@ -18,11 +18,11 @@ pub struct u256 {
|
||||
}
|
||||
|
||||
impl u256 {
|
||||
#[cfg(any(test, feature = "unstable-public-internals"))]
|
||||
pub const MAX: Self = Self {
|
||||
lo: u128::MAX,
|
||||
hi: u128::MAX,
|
||||
};
|
||||
pub const MIN: Self = Self { lo: 0, hi: 0 };
|
||||
|
||||
/// Reinterpret as a signed integer
|
||||
pub fn signed(self) -> i256 {
|
||||
@@ -42,6 +42,15 @@ pub struct i256 {
|
||||
}
|
||||
|
||||
impl i256 {
|
||||
pub const MAX: Self = Self {
|
||||
lo: u128::MAX,
|
||||
hi: i128::MAX,
|
||||
};
|
||||
pub const MIN: Self = Self {
|
||||
lo: u128::MIN,
|
||||
hi: i128::MIN,
|
||||
};
|
||||
|
||||
/// Reinterpret as an unsigned integer
|
||||
pub fn unsigned(self) -> u256 {
|
||||
u256 {
|
||||
@@ -60,11 +69,8 @@ impl MinInt for u256 {
|
||||
const BITS: u32 = 256;
|
||||
const ZERO: Self = Self { lo: 0, hi: 0 };
|
||||
const ONE: Self = Self { lo: 1, hi: 0 };
|
||||
const MIN: Self = Self { lo: 0, hi: 0 };
|
||||
const MAX: Self = Self {
|
||||
lo: u128::MAX,
|
||||
hi: u128::MAX,
|
||||
};
|
||||
const MIN: Self = Self::MIN;
|
||||
const MAX: Self = Self::MAX;
|
||||
}
|
||||
|
||||
impl MinInt for i256 {
|
||||
@@ -76,14 +82,8 @@ impl MinInt for i256 {
|
||||
const BITS: u32 = 256;
|
||||
const ZERO: Self = Self { lo: 0, hi: 0 };
|
||||
const ONE: Self = Self { lo: 1, hi: 0 };
|
||||
const MIN: Self = Self {
|
||||
lo: u128::MIN,
|
||||
hi: i128::MIN,
|
||||
};
|
||||
const MAX: Self = Self {
|
||||
lo: u128::MAX,
|
||||
hi: i128::MAX,
|
||||
};
|
||||
const MIN: Self = Self::MIN;
|
||||
const MAX: Self = Self::MAX;
|
||||
}
|
||||
|
||||
macro_rules! impl_common {
|
||||
|
||||
@@ -113,13 +113,10 @@ fn shl_u256() {
|
||||
has_errors = true;
|
||||
eprintln!(
|
||||
"\
|
||||
FAILURE: {} << {b}\n\
|
||||
expected: {}\n\
|
||||
actual: {}\
|
||||
FAILURE: {a:#x} << {b}\n\
|
||||
expected: {expected:#x}\n\
|
||||
actual: {actual:#x}\
|
||||
",
|
||||
hexu(a),
|
||||
hexu(expected),
|
||||
hexu(actual),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user