use super::int::BigInt;
use super::uint::BigUint;
use core::ops::{BitAnd, BitOr, BitXor, 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));
}
}