use super::int::BigInt;
use super::uint::BigUint;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, Shr};
use oxinum_core::Sign;
fn to_twos_complement(n: &BigInt, nlimbs: usize) -> Vec<u64> {
let mag = n.magnitude().as_limbs();
let mut out = vec![0u64; nlimbs];
let copy_len = mag.len().min(nlimbs);
out[..copy_len].copy_from_slice(&mag[..copy_len]);
if n.is_negative() {
for limb in out.iter_mut() {
*limb = !*limb;
}
let mut carry: u64 = 1;
for limb in out.iter_mut() {
let (v, c) = limb.overflowing_add(carry);
*limb = v;
carry = c as u64;
if carry == 0 {
break;
}
}
}
out
}
fn from_twos_complement(limbs: &[u64]) -> BigInt {
if limbs.is_empty() {
return BigInt::zero();
}
let top = limbs[limbs.len() - 1];
if top >> 63 == 0 {
BigInt::from_parts(Sign::Positive, BigUint::from_le_limbs(limbs))
} else {
let mut neg: Vec<u64> = limbs.to_vec();
for v in neg.iter_mut() {
*v = !*v;
}
let mut carry: u64 = 1;
for v in neg.iter_mut() {
let (nv, c) = v.overflowing_add(carry);
*v = nv;
carry = c as u64;
if carry == 0 {
break;
}
}
BigInt::from_parts(Sign::Negative, BigUint::from_le_limbs(&neg))
}
}
#[inline]
fn bigint_binop<F>(lhs: &BigInt, rhs: &BigInt, op: F) -> BigInt
where
F: Fn(u64, u64) -> u64,
{
let llen = lhs.magnitude().as_limbs().len();
let rlen = rhs.magnitude().as_limbs().len();
let nlimbs = llen.max(rlen) + 1;
let ltc = to_twos_complement(lhs, nlimbs);
let rtc = to_twos_complement(rhs, nlimbs);
let result: Vec<u64> = ltc
.iter()
.zip(rtc.iter())
.map(|(&a, &b)| op(a, b))
.collect();
from_twos_complement(&result)
}
impl BitAnd<&BigInt> for &BigInt {
type Output = BigInt;
#[inline]
fn bitand(self, rhs: &BigInt) -> BigInt {
bigint_binop(self, rhs, |a, b| a & b)
}
}
impl BitAnd<BigInt> for BigInt {
type Output = BigInt;
#[inline]
fn bitand(self, rhs: BigInt) -> BigInt {
bigint_binop(&self, &rhs, |a, b| a & b)
}
}
impl BitAnd<&BigInt> for BigInt {
type Output = BigInt;
#[inline]
fn bitand(self, rhs: &BigInt) -> BigInt {
bigint_binop(&self, rhs, |a, b| a & b)
}
}
impl BitAnd<BigInt> for &BigInt {
type Output = BigInt;
#[inline]
fn bitand(self, rhs: BigInt) -> BigInt {
bigint_binop(self, &rhs, |a, b| a & b)
}
}
impl BitOr<&BigInt> for &BigInt {
type Output = BigInt;
#[inline]
fn bitor(self, rhs: &BigInt) -> BigInt {
bigint_binop(self, rhs, |a, b| a | b)
}
}
impl BitOr<BigInt> for BigInt {
type Output = BigInt;
#[inline]
fn bitor(self, rhs: BigInt) -> BigInt {
bigint_binop(&self, &rhs, |a, b| a | b)
}
}
impl BitOr<&BigInt> for BigInt {
type Output = BigInt;
#[inline]
fn bitor(self, rhs: &BigInt) -> BigInt {
bigint_binop(&self, rhs, |a, b| a | b)
}
}
impl BitOr<BigInt> for &BigInt {
type Output = BigInt;
#[inline]
fn bitor(self, rhs: BigInt) -> BigInt {
bigint_binop(self, &rhs, |a, b| a | b)
}
}
impl BitXor<&BigInt> for &BigInt {
type Output = BigInt;
#[inline]
fn bitxor(self, rhs: &BigInt) -> BigInt {
bigint_binop(self, rhs, |a, b| a ^ b)
}
}
impl BitXor<BigInt> for BigInt {
type Output = BigInt;
#[inline]
fn bitxor(self, rhs: BigInt) -> BigInt {
bigint_binop(&self, &rhs, |a, b| a ^ b)
}
}
impl BitXor<&BigInt> for BigInt {
type Output = BigInt;
#[inline]
fn bitxor(self, rhs: &BigInt) -> BigInt {
bigint_binop(&self, rhs, |a, b| a ^ b)
}
}
impl BitXor<BigInt> for &BigInt {
type Output = BigInt;
#[inline]
fn bitxor(self, rhs: BigInt) -> BigInt {
bigint_binop(self, &rhs, |a, b| a ^ b)
}
}
impl Not for BigInt {
type Output = BigInt;
fn not(self) -> BigInt {
if self.is_negative() {
let m = self.magnitude().clone();
let one = BigUint::one();
match m.checked_sub(&one) {
Some(result) => BigInt::from_parts(Sign::Positive, result),
None => BigInt::zero(),
}
} else {
let mag_plus_one = self.magnitude() + &BigUint::one();
BigInt::from_parts(Sign::Negative, mag_plus_one)
}
}
}
impl Not for &BigInt {
type Output = BigInt;
#[inline]
fn not(self) -> BigInt {
self.clone().not()
}
}
impl Shl<u64> for BigInt {
type Output = BigInt;
fn shl(self, k: u64) -> BigInt {
if self.is_zero() || k == 0 {
return self;
}
let sign = self.sign();
let shifted_mag = self.magnitude().shl_bits(k);
BigInt::from_parts(sign, shifted_mag)
}
}
impl Shl<u64> for &BigInt {
type Output = BigInt;
#[inline]
fn shl(self, k: u64) -> BigInt {
self.clone().shl(k)
}
}
impl Shr<u64> for BigInt {
type Output = BigInt;
fn shr(self, k: u64) -> BigInt {
if k == 0 {
return self;
}
if self.is_negative() {
let m = self.magnitude().clone();
let m_minus_one = m.checked_sub(&BigUint::one()).unwrap_or_else(BigUint::zero);
let shifted = m_minus_one.shr_bits(k);
let mag = &shifted + &BigUint::one();
BigInt::from_parts(Sign::Negative, mag)
} else {
BigInt::from_parts(Sign::Positive, self.magnitude().shr_bits(k))
}
}
}
impl Shr<u64> for &BigInt {
type Output = BigInt;
#[inline]
fn shr(self, k: u64) -> BigInt {
self.clone().shr(k)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn bi(v: i64) -> BigInt {
BigInt::from(v)
}
fn bu(v: u64) -> BigUint {
BigUint::from_u64(v)
}
#[test]
fn not_basic() {
assert_eq!(!bi(0), bi(-1));
assert_eq!(!bi(5), bi(-6));
assert_eq!(!bi(-1), bi(0));
assert_eq!(!bi(-6), bi(5));
}
#[test]
fn not_double_negation() {
for v in [-1000i64, -1, 0, 1, 1000] {
let n = bi(v);
assert_eq!(!!n.clone(), n, "!!n == n failed for {v}");
}
}
#[test]
fn and_neg_one_with_ff() {
let neg1 = bi(-1);
let ff = bi(0xFF);
assert_eq!(&neg1 & &ff, ff);
}
#[test]
fn or_neg_one_is_neg_one() {
let neg1 = bi(-1);
let ff = bi(0xFF);
assert_eq!(&neg1 | &ff, neg1);
}
#[test]
fn xor_self_is_zero() {
let neg1 = bi(-1);
assert_eq!(&neg1 ^ &neg1, BigInt::zero());
let x = bi(-12345);
assert_eq!(&x ^ &x, BigInt::zero());
}
#[test]
fn shr_negative_floor_div() {
assert_eq!(bi(-8) >> 1u64, bi(-4));
assert_eq!(bi(-7) >> 1u64, bi(-4));
assert_eq!(bi(-1) >> 1u64, bi(-1));
assert_eq!(bi(-1) >> 100u64, bi(-1));
assert_eq!(bi(7) >> 1u64, bi(3));
}
#[test]
fn shl_signed() {
assert_eq!(bi(1) << 4u64, bi(16));
assert_eq!(bi(-1) << 4u64, bi(-16));
assert_eq!(bi(0) << 100u64, BigInt::zero());
}
#[test]
fn to_twos_complement_positive() {
let n = bi(5);
let tc = to_twos_complement(&n, 2);
assert_eq!(tc, vec![5, 0]);
}
#[test]
fn to_twos_complement_negative_one() {
let n = bi(-1);
let tc = to_twos_complement(&n, 2);
assert_eq!(tc, vec![u64::MAX, u64::MAX]);
}
#[test]
fn from_twos_complement_roundtrip() {
for v in [-1i128, -2, -128, 0, 1, 127, 1000] {
let n = BigInt::from(v);
let tc = to_twos_complement(&n, 4);
let back = from_twos_complement(&tc);
assert_eq!(back, n, "roundtrip failed for {v}");
}
}
#[test]
fn de_morgan_and_to_or() {
let pairs: &[(i64, i64)] = &[
(0, 0),
(5, 3),
(-5, 3),
(5, -3),
(-5, -3),
(-1, 0xFF),
(i64::MAX, i64::MIN),
];
for &(av, bv) in pairs {
let a = bi(av);
let b = bi(bv);
let lhs = !(&a & &b);
let rhs = !a.clone() | !b.clone();
assert_eq!(lhs, rhs, "De Morgan failed for ({av}, {bv})");
}
}
#[test]
fn de_morgan_or_to_and() {
let pairs: &[(i64, i64)] = &[(0, 0), (5, 3), (-5, 3), (5, -3), (-5, -3), (-1, 0xFF)];
for &(av, bv) in pairs {
let a = bi(av);
let b = bi(bv);
let lhs = !(&a | &b);
let rhs = !a.clone() & !b.clone();
assert_eq!(lhs, rhs, "De Morgan (or→and) failed for ({av}, {bv})");
}
}
#[test]
fn i128_cross_val_bitwise_and_shifts() {
let vals: &[i128] = &[
0,
1,
-1,
127,
-128,
1000,
-1000,
i64::MAX as i128,
i64::MIN as i128,
];
for &i in vals {
let a = BigInt::from(i);
assert_eq!(!a.clone(), BigInt::from(!i), "!{i} mismatch");
for j in 0i128..20 {
let b = BigInt::from(j);
assert_eq!(&a & &b, BigInt::from(i & j), "{i} & {j} mismatch");
assert_eq!(&a | &b, BigInt::from(i | j), "{i} | {j} mismatch");
assert_eq!(&a ^ &b, BigInt::from(i ^ j), "{i} ^ {j} mismatch");
assert_eq!(
a.clone() >> (j as u64),
BigInt::from(i >> j),
"{i} >> {j} mismatch"
);
}
for &j in vals {
assert_eq!(
&a & &BigInt::from(j),
BigInt::from(i & j),
"{i} & {j} mismatch"
);
assert_eq!(
&a | &BigInt::from(j),
BigInt::from(i | j),
"{i} | {j} mismatch"
);
assert_eq!(
&a ^ &BigInt::from(j),
BigInt::from(i ^ j),
"{i} ^ {j} mismatch"
);
}
}
}
#[test]
fn xor_identity_and_complement() {
for v in [-5i64, -1, 0, 1, 5] {
let n = bi(v);
assert_eq!(&n ^ &BigInt::zero(), n.clone());
assert_eq!(&n ^ &n, BigInt::zero());
let all_ones = bi(-1);
assert_eq!(&n ^ &all_ones, !n.clone());
}
}
#[test]
fn shr_positive_matches_biguint_shr() {
let mag = BigUint::from_u64(1234567890);
let n = BigInt::from_parts(Sign::Positive, mag.clone());
for k in [0u64, 1, 7, 15, 31, 63, 64, 65] {
let expected = BigInt::from_parts(Sign::Positive, mag.shr_bits(k));
assert_eq!(n.clone() >> k, expected, "positive shr mismatch for k={k}");
}
}
#[test]
fn bu_unused() {
assert_eq!(bu(42), BigUint::from_u64(42));
}
}
impl BitAndAssign<BigUint> for BigUint {
#[inline]
fn bitand_assign(&mut self, rhs: BigUint) {
*self = &*self & &rhs;
}
}
impl BitAndAssign<&BigUint> for BigUint {
#[inline]
fn bitand_assign(&mut self, rhs: &BigUint) {
*self = &*self & rhs;
}
}
impl BitOrAssign<BigUint> for BigUint {
#[inline]
fn bitor_assign(&mut self, rhs: BigUint) {
*self = &*self | &rhs;
}
}
impl BitOrAssign<&BigUint> for BigUint {
#[inline]
fn bitor_assign(&mut self, rhs: &BigUint) {
*self = &*self | rhs;
}
}
impl BitXorAssign<BigUint> for BigUint {
#[inline]
fn bitxor_assign(&mut self, rhs: BigUint) {
*self = &*self ^ &rhs;
}
}
impl BitXorAssign<&BigUint> for BigUint {
#[inline]
fn bitxor_assign(&mut self, rhs: &BigUint) {
*self = &*self ^ rhs;
}
}
#[cfg(test)]
mod biguint_bitwise_tests {
use super::*;
fn bu(v: u64) -> BigUint {
BigUint::from_u64(v)
}
#[test]
fn and_basic() {
assert_eq!(&bu(0b1100) & &bu(0b1010), bu(0b1000));
assert_eq!(&bu(0xFF) & &bu(0x0F), bu(0x0F));
assert_eq!(&bu(0) & &bu(0xFF), bu(0));
}
#[test]
fn and_zero_identity() {
let x = bu(0xDEAD_BEEF);
assert_eq!(&x & &bu(0), bu(0), "a & 0 == 0");
}
#[test]
fn and_self_identity() {
let x = bu(0xDEAD_BEEF);
assert_eq!(&x & &x, x.clone(), "a & a == a");
}
#[test]
fn and_unequal_limbs() {
let mut limbs_a = vec![0xFFFF_FFFF_FFFF_FFFFu64, 0xFFFF_FFFF_FFFF_FFFFu64];
let limbs_b = vec![0xAAAA_AAAA_AAAA_AAAAu64];
let a = BigUint::from_le_limbs(&limbs_a);
let b = BigUint::from_le_limbs(&limbs_b);
let result = &a & &b;
assert_eq!(result, BigUint::from_le_limbs(&limbs_b));
limbs_a[1] = 0;
let a2 = BigUint::from_le_limbs(&limbs_a);
let result2 = &a2 & &b;
assert_eq!(result2, BigUint::from_le_limbs(&limbs_b));
}
#[test]
fn and_assign() {
let mut x = bu(0b1111);
x &= bu(0b1010);
assert_eq!(x, bu(0b1010));
}
#[test]
fn or_basic() {
assert_eq!(&bu(0b1100) | &bu(0b1010), bu(0b1110));
assert_eq!(&bu(0xF0) | &bu(0x0F), bu(0xFF));
}
#[test]
fn or_zero_identity() {
let x = bu(0xDEAD_BEEF);
assert_eq!(&x | &bu(0), x.clone(), "a | 0 == a");
}
#[test]
fn or_self_identity() {
let x = bu(0xDEAD_BEEF);
assert_eq!(&x | &x, x.clone(), "a | a == a");
}
#[test]
fn or_unequal_limbs() {
let limbs_a = vec![0x1111_1111_1111_1111u64, 0x2222_2222_2222_2222u64];
let limbs_b = vec![0x4444_4444_4444_4444u64];
let a = BigUint::from_le_limbs(&limbs_a);
let b = BigUint::from_le_limbs(&limbs_b);
let result = &a | &b;
let expected = BigUint::from_le_limbs(&[
0x1111_1111_1111_1111u64 | 0x4444_4444_4444_4444u64,
0x2222_2222_2222_2222u64,
]);
assert_eq!(result, expected);
}
#[test]
fn or_assign() {
let mut x = bu(0b1010);
x |= bu(0b0101);
assert_eq!(x, bu(0b1111));
}
#[test]
fn xor_basic() {
assert_eq!(&bu(0b1100) ^ &bu(0b1010), bu(0b0110));
assert_eq!(&bu(0xFF) ^ &bu(0x0F), bu(0xF0));
}
#[test]
fn xor_self_is_zero() {
let x = bu(0xDEAD_BEEF_CAFE_BABEu64);
assert_eq!(&x ^ &x, bu(0), "a ^ a == 0");
}
#[test]
fn xor_zero_identity() {
let x = bu(0xDEAD_BEEF);
assert_eq!(&x ^ &bu(0), x.clone(), "a ^ 0 == a");
}
#[test]
fn xor_unequal_limbs() {
let limbs_a = vec![0xFFFF_FFFF_FFFF_FFFFu64, 0xFFFF_FFFF_FFFF_FFFFu64];
let limbs_b = vec![0xFFFF_FFFF_FFFF_FFFFu64];
let a = BigUint::from_le_limbs(&limbs_a);
let b = BigUint::from_le_limbs(&limbs_b);
let result = &a ^ &b;
let expected = BigUint::from_le_limbs(&[0u64, 0xFFFF_FFFF_FFFF_FFFFu64]);
assert_eq!(result, expected);
}
#[test]
fn xor_double_is_identity() {
let a = BigUint::from_le_limbs(&[0xDEAD_BEEF, 0xCAFE_BABE, 0x1234_5678]);
let b = BigUint::from_le_limbs(&[0x1111_2222, 0x3333_4444]);
let c = &(&a ^ &b) ^ &b;
assert_eq!(c, a);
}
#[test]
fn xor_normalization() {
let big = BigUint::from_le_limbs(&[
0xAAAA_BBBB_CCCC_DDDDu64,
0xEEEE_FFFF_0000_1111u64,
0x2222_3333_4444_5555u64,
]);
let result: BigUint = &big ^ &big;
assert_eq!(result, BigUint::zero());
assert!(result.is_zero());
}
#[test]
fn xor_assign() {
let mut x = bu(0b1111);
x ^= bu(0b1010);
assert_eq!(x, bu(0b0101));
}
#[test]
fn owned_ops() {
let a = bu(0b1110);
let b = bu(0b1011);
assert_eq!(a.clone() & b.clone(), bu(0b1010));
assert_eq!(a.clone() | b.clone(), bu(0b1111));
assert_eq!(a.clone() ^ b.clone(), bu(0b0101));
}
}