crate::ix!();
#[macro_export]
macro_rules! define_baseuint_bitand {
($uint_type:ident, $bits:expr, $limbs:expr) => {
impl core::ops::BitAnd<&$uint_type> for $uint_type {
type Output = $uint_type;
fn bitand(self, other: &$uint_type) -> Self::Output {
let mut ret = self.clone();
ret &= other;
ret
}
}
impl core::ops::BitAndAssign<&$uint_type> for $uint_type {
#[inline]
fn bitand_assign(&mut self, b: &$uint_type) {
for i in 0..$limbs {
self.pn[i] &= b.pn[i];
}
}
}
impl core::ops::BitAndAssign<u64> for $uint_type {
fn bitand_assign(&mut self, b: u64) {
self.pn[0] &= (b & 0xffff_ffff) as u32;
if $limbs > 1 {
self.pn[1] &= ((b >> 32) & 0xffff_ffff) as u32;
}
for i in 2..$limbs {
self.pn[i] = 0;
}
}
}
}
}
#[cfg(test)]
mod bitwise_and_exhaustive_tests {
use super::*;
use tracing::{debug, error, info, trace};
use std::cmp::Ordering;
#[traced_test]
fn test_bitand_assign_basic_64() {
info!("Verifying basic AND assignment (self &= other) with 64-bit BaseUInt.");
type U64 = BaseUInt64;
let mut x = U64::default(); let y = U64::from(0xFFFF_FFFF_1234_5678u64);
trace!("x = 0x{:016X}, y = 0x{:016X}", x.low64(), y.low64());
x &= &y;
assert_eq!(x.low64(), 0, "0 & anything => 0");
let mut all_ones = U64::default();
all_ones.pn[0] = 0xFFFF_FFFF;
all_ones.pn[1] = 0xFFFF_FFFF;
trace!("all_ones = {:?}", all_ones);
let partial = U64::from(0xAABB_CCDD_1122_3344u64);
trace!("partial = {:?}", partial);
let expected_and = 0xAABB_CCDD_1122_3344u64; all_ones &= &partial;
trace!("Result after &= partial => {:?}", all_ones);
assert_eq!(all_ones.low64(), expected_and);
let mut a = U64::from(0xFFFF_0000u64);
let b = U64::from(0x1234_FFFFu64);
a &= &b;
let res = a.low64();
trace!("0xFFFF0000 & 0x1234FFFF => 0x{:08X}", res as u32);
assert_eq!(res, 0x1234_0000);
info!("Basic AND assignment test (64-bit) passed.");
}
#[traced_test]
fn test_bitand_assign_random_64() {
info!("Testing random AND assignment for 64-bit BaseUInt.");
type U64 = BaseUInt64;
let mut rng = SimpleLCG::new(0xBEEF_0000_DEAD_0000);
for i in 0..20 {
let a_val = rng.next_u64();
let b_val = rng.next_u64();
let mut a = U64::from(a_val);
let b = U64::from(b_val);
let expected = a_val & b_val; trace!("Iter {}: a=0x{:016X}, b=0x{:016X}, a&b=0x{:016X}", i, a_val, b_val, expected);
a &= &b;
let got = a.low64();
assert_eq!(got, expected, "Mismatch in random 64-bit &=");
}
info!("Random AND assignment test (64-bit) succeeded.");
}
#[traced_test]
fn test_bitand_assign_256_exhaustive_edges() {
info!("Testing bitwise AND assignment for a few edge cases in 256-bit BaseUInt.");
type U256 = BaseUInt256;
let mut z1 = U256::default();
let z2 = U256::default();
z1 &= &z2;
for &limb in z1.pn.iter() {
assert_eq!(limb, 0, "0 & 0 => 0 for each limb");
}
let mut zero = U256::default();
let mut full = U256::default();
for i in 0..8 {
full.pn[i] = 0xFFFF_FFFF;
}
zero &= &full;
for &limb in zero.pn.iter() {
assert_eq!(limb, 0);
}
let mut a = U256::default();
let mut b = U256::default();
a.pn[0] = 0x1111_2222;
a.pn[1] = 0x3333_4444;
a.pn[2] = 0x5555_6666;
a.pn[3] = 0x7777_8888;
a.pn[4] = 0x9999_AAAA;
a.pn[5] = 0xBBBB_CCCC;
a.pn[6] = 0xDDDD_EEEE;
a.pn[7] = 0xFFFF_0000;
for i in 0..8 {
b.pn[i] = 0xFFFF_0000;
}
let mut copy_a = a.clone();
copy_a &= &b;
trace!("After &= b => copy_a = {:?}", copy_a);
for i in 0..8 {
let expected = a.pn[i] & 0xFFFF_0000;
assert_eq!(copy_a.pn[i], expected, "Mismatch limb {i} in 256-bit AND");
}
info!("Edge-case coverage for 256-bit AND assignment passed.");
}
#[traced_test]
fn test_bitand_operator_256_random() {
info!("Random test of bitwise AND operator for 256-bit BaseUInt.");
type U256 = BaseUInt256;
let mut rng = SimpleLCG::new(0x1234_5678_DEAD_BEEF);
for i in 0..10 {
let mut limbs_a = [0u32; 8];
let mut limbs_b = [0u32; 8];
for j in 0..8 {
limbs_a[j] = rng.next_u64() as u32;
limbs_b[j] = rng.next_u64() as u32;
}
let a = from_limbs_256(&limbs_a);
let b = from_limbs_256(&limbs_b);
trace!("Test iteration {} => a={:?}, b={:?}", i, a, b);
let c = a.clone() & &b;
let c_limbs = to_limbs_256(&c);
for (idx, &val_c) in c_limbs.iter().enumerate() {
let expected = limbs_a[idx] & limbs_b[idx];
assert_eq!(val_c, expected, "Mismatch in random 256-bit & for limb {idx}");
}
}
info!("Random operator (&) checks passed for 256-bit BaseUInt.");
}
#[traced_test]
fn test_bitand_operator_64() {
info!("Testing the bitand operator (self & &other) in 64-bit BaseUInt.");
type U64 = BaseUInt64;
let x = U64::from(0xDEAD_BEEF_1234_5678u64);
let y = U64::from(0xFFFF_0000_FFFF_0000u64);
let result = x.clone() & &y;
let expected = 0xDEAD_0000_1234_0000u64;
trace!("x={:?}, y={:?}, result={:?}, expected=0x{:016X}", x, y, result, expected);
assert_eq!(result.low64(), expected);
let a = U64::from(0x0123_4567_89AB_CDEFu64);
let b = U64::from(0xF0F0_F0F0_F0F0_F0F0u64);
let c = a.clone() & &b;
let exp = 0x0020_4060_80A0_C0E0u64;
trace!(
"a=0x{:016X}, b=0x{:016X}, a&b=0x{:016X}, exp=0x{:016X}",
a.low64(),
b.low64(),
c.low64(),
exp
);
assert_eq!(c.low64(), exp);
info!("Bitwise AND operator test (64-bit) passed.");
}
struct SimpleRng(u64);
impl SimpleRng {
fn new(seed: u64) -> Self {
Self(seed)
}
fn next_u64(&mut self) -> u64 {
self.0 = self.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1);
self.0
}
}
#[test]
fn test_bitand_assign_u64_basics() {
{
let mut x = BaseUInt64::default(); x &= 0xFFFF_FFFF_FFFF_FFFFu64; assert_eq!(x.pn[0], 0, "0 & allones => 0, lower-limb");
assert_eq!(x.pn[1], 0, "0 & allones => 0, upper-limb");
}
{
let mut x = BaseUInt64::default();
x.pn[0] = 0xFFFF_FFFF;
x.pn[1] = 0xFFFF_FFFF;
let mask = 0x0000_FFFF_1234_5678u64;
x &= mask;
let expected = 0xFFFF_FFFF_FFFF_FFFFu64 & mask;
let low32 = (expected & 0xFFFF_FFFF) as u32;
let high32 = ((expected >> 32) & 0xFFFF_FFFF) as u32;
assert_eq!(x.pn[0], low32);
assert_eq!(x.pn[1], high32);
}
{
let mut x = BaseUInt256::default();
x.pn[0] = 0xDEAD_BEEF;
x.pn[1] = 0xAAAA_5555;
x.pn[2] = 0xFF00_FF00;
x.pn[3] = 0x1234_5678;
x &= 0xFFFF_FFFF_0000_1111u64;
let expected64 = (0xFFFF_FFFF_0000_1111u64) & (
(x.pn[0] as u64)
| ((x.pn[1] as u64) << 32)
);
let ex_low = (expected64 & 0xFFFF_FFFF) as u32;
let ex_high = ((expected64 >> 32) & 0xFFFF_FFFF) as u32;
assert_eq!(x.pn[0], ex_low);
assert_eq!(x.pn[1], ex_high);
for i in 2..8 {
assert_eq!(x.pn[i], 0);
}
}
}
#[test]
fn test_bitand_assign_u64_random() {
let mut rng = SimpleRng::new(0xDEAD_BEEF_1234_5678);
for _ in 0..100 {
let random_a = rng.next_u64();
let random_mask = rng.next_u64();
let mut x = BaseUInt64::from(random_a);
let expected_64 = random_a & random_mask;
x &= random_mask;
let low32 = (expected_64 & 0xffff_ffff) as u32;
let high32 = ((expected_64 >> 32) & 0xffff_ffff) as u32;
assert_eq!(x.pn[0], low32);
assert_eq!(x.pn[1], high32);
}
for _ in 0..20 {
let mut big = BaseUInt256::default();
for i in 0..8 {
big.pn[i] = rng.next_u64() as u32;
}
let mask64 = rng.next_u64();
let mut expected = big.clone();
let orig_low64 = (expected.pn[0] as u64)
| ((expected.pn[1] as u64) << 32);
let new_low64 = orig_low64 & mask64;
expected.pn[0] = (new_low64 & 0xffff_ffff) as u32;
expected.pn[1] = ((new_low64 >> 32) & 0xffff_ffff) as u32;
for i in 2..8 {
expected.pn[i] = 0;
}
let mut actual = big.clone();
actual &= mask64;
assert_eq!(actual.get_hex(), expected.get_hex(),
"bitand_assign<u64> mismatch in 256-bit random test.\n big was {},\n mask64=0x{:016X}",
big.get_hex(),
mask64);
}
}
}