From 3f1aa0b47eb16ebf049436ca0d5a16ea250f45d7 Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Mon, 1 Dec 2025 20:37:11 +0200 Subject: [PATCH 1/2] Additional test for uN::{gather,scatter}_bits --- library/coretests/tests/num/uint_macros.rs | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index 7f3e27e9c446c..6e746a6a2278d 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -387,6 +387,70 @@ macro_rules! uint_module { } } + #[cfg(not(miri))] // Miri is too slow + #[test] + fn test_lots_of_gather_scatter() { + // Generate a handful of bit patterns to use as inputs + let xs = { + let mut xs = vec![]; + let mut x: $T = !0; + let mut w = $T::BITS; + while w > 0 { + w >>= 1; + xs.push(x); + xs.push(!x); + x ^= x << w; + } + xs + }; + if $T::BITS == 8 { + assert_eq!(&xs, &[0xff, 0x00, 0x0f, 0xf0, 0x33, 0xcc, 0x55, 0xaa]); + } + + // `256 * BITS` masks + let sparse_masks = (i8::MIN..=i8::MAX) + .map(|i| (i as i128 as $T).rotate_right(4)) + .flat_map(|x| (0..$T::BITS).map(move |s| ((1 as $T) << s) ^ x)); + + for sparse in sparse_masks { + // Collect the set bits to sequential low bits + let dense = sparse.gather_bits(sparse); + let count = sparse.count_ones(); + assert_eq!(count, dense.count_ones()); + assert_eq!(count, dense.trailing_ones()); + + let mut t = sparse; + for k in 0..$T::BITS { + let x = ((1 as $T) << k).scatter_bits(sparse); + let y = t.isolate_lowest_one(); + assert_eq!(x, y); + t ^= y; + } + + let mut t = sparse; + for k in 0..count { + let y = t.isolate_lowest_one(); + let x = y.gather_bits(sparse); + assert_eq!(x, (1 as $T) << k); + t ^= y; + } + + for &x in &xs { + // Gather bits from `x & sparse` to `dense` + let dx = x.gather_bits(sparse); + assert_eq!(dx & !dense, 0); + + // Scatter bits from `x & dense` to `sparse` + let sx = x.scatter_bits(sparse); + assert_eq!(sx & !sparse, 0); + + // The other recovers the input (within the mask) + assert_eq!(dx.scatter_bits(sparse), x & sparse); + assert_eq!(sx.gather_bits(sparse), x & dense); + } + } + } + test_runtime_and_compiletime! { fn test_div_floor() { assert_eq_const_safe!($T: (8 as $T).div_floor(3), 2); From f49eaecca9e231ffe0f290f31314be06ef5f8db2 Mon Sep 17 00:00:00 2001 From: quaternic <57393910+quaternic@users.noreply.github.com> Date: Wed, 3 Dec 2025 10:08:29 +0200 Subject: [PATCH 2/2] reorganize test contents and adjust generated inputs to reduce iterations --- library/coretests/tests/num/uint_macros.rs | 28 ++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index 6e746a6a2278d..72d500e575fa0 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -407,10 +407,10 @@ macro_rules! uint_module { assert_eq!(&xs, &[0xff, 0x00, 0x0f, 0xf0, 0x33, 0xcc, 0x55, 0xaa]); } - // `256 * BITS` masks + // `256 * (BITS / 5)` masks let sparse_masks = (i8::MIN..=i8::MAX) .map(|i| (i as i128 as $T).rotate_right(4)) - .flat_map(|x| (0..$T::BITS).map(move |s| ((1 as $T) << s) ^ x)); + .flat_map(|x| (0..$T::BITS).step_by(5).map(move |r| x.rotate_right(r))); for sparse in sparse_masks { // Collect the set bits to sequential low bits @@ -419,21 +419,19 @@ macro_rules! uint_module { assert_eq!(count, dense.count_ones()); assert_eq!(count, dense.trailing_ones()); + // Check that each bit is individually mapped correctly let mut t = sparse; - for k in 0..$T::BITS { - let x = ((1 as $T) << k).scatter_bits(sparse); - let y = t.isolate_lowest_one(); - assert_eq!(x, y); - t ^= y; - } - - let mut t = sparse; - for k in 0..count { - let y = t.isolate_lowest_one(); - let x = y.gather_bits(sparse); - assert_eq!(x, (1 as $T) << k); - t ^= y; + let mut bit = 1 as $T; + for _ in 0..count { + let lowest_one = t.isolate_lowest_one(); + assert_eq!(lowest_one, bit.scatter_bits(sparse)); + assert_eq!(bit, lowest_one.gather_bits(sparse)); + t ^= lowest_one; + bit <<= 1; } + // Other bits are ignored + assert_eq!(0, bit.wrapping_neg().scatter_bits(sparse)); + assert_eq!(0, (!sparse).gather_bits(sparse)); for &x in &xs { // Gather bits from `x & sparse` to `dense`