diff --git a/src/limb.rs b/src/limb.rs index 4f37a53b..f61f3634 100644 --- a/src/limb.rs +++ b/src/limb.rs @@ -5,7 +5,9 @@ mod add; mod bit_and; +mod bit_not; mod bit_or; +mod bit_xor; mod cmp; mod encoding; mod from; diff --git a/src/limb/bit_not.rs b/src/limb/bit_not.rs new file mode 100644 index 00000000..26676d59 --- /dev/null +++ b/src/limb/bit_not.rs @@ -0,0 +1,19 @@ +//! Limb bit not operations. + +use super::Limb; +use core::ops::Not; + +impl Limb { + /// Calculates `!a`. + pub const fn not(self) -> Self { + Limb(!self.0) + } +} + +impl Not for Limb { + type Output = Limb; + + fn not(self) -> ::Output { + self.not() + } +} diff --git a/src/limb/bit_xor.rs b/src/limb/bit_xor.rs new file mode 100644 index 00000000..a5078229 --- /dev/null +++ b/src/limb/bit_xor.rs @@ -0,0 +1,19 @@ +//! Limb bit xor operations. + +use super::Limb; +use core::ops::BitXor; + +impl Limb { + /// Calculates `a ^ b`. + pub const fn bitxor(self, rhs: Self) -> Self { + Limb(self.0 ^ rhs.0) + } +} + +impl BitXor for Limb { + type Output = Limb; + + fn bitxor(self, rhs: Self) -> Self::Output { + self.bitxor(rhs) + } +} diff --git a/src/uint.rs b/src/uint.rs index a975ac55..96b067fc 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -11,14 +11,16 @@ mod macros; mod add; mod add_mod; -mod and; +mod bit_and; +mod bit_not; +mod bit_or; +mod bit_xor; mod cmp; mod div; mod encoding; mod from; mod mul; mod neg_mod; -mod or; mod shl; mod shr; mod sqrt; diff --git a/src/uint/and.rs b/src/uint/bit_and.rs similarity index 100% rename from src/uint/and.rs rename to src/uint/bit_and.rs diff --git a/src/uint/bit_not.rs b/src/uint/bit_not.rs new file mode 100644 index 00000000..8d5dfe23 --- /dev/null +++ b/src/uint/bit_not.rs @@ -0,0 +1,40 @@ +//! [`UInt`] bitwise not operations. + +use super::UInt; +use crate::{Limb, Wrapping}; +use core::ops::Not; + +impl UInt { + /// Computes bitwise `!a`. + #[inline(always)] + pub const fn not(&self) -> Self { + let mut limbs = [Limb::ZERO; LIMBS]; + let mut i = 0; + + while i < LIMBS { + limbs[i] = self.limbs[i].not(); + i += 1; + } + + Self { limbs } + } +} + +impl Not for Wrapping> { + type Output = Self; + + fn not(self) -> ::Output { + Wrapping(self.0.not()) + } +} + +#[cfg(test)] +mod tests { + use crate::U128; + + #[test] + fn bitnot_ok() { + assert_eq!(U128::ZERO.not(), U128::MAX); + assert_eq!(U128::MAX.not(), U128::ZERO); + } +} diff --git a/src/uint/or.rs b/src/uint/bit_or.rs similarity index 100% rename from src/uint/or.rs rename to src/uint/bit_or.rs diff --git a/src/uint/bit_xor.rs b/src/uint/bit_xor.rs new file mode 100644 index 00000000..96dbc10a --- /dev/null +++ b/src/uint/bit_xor.rs @@ -0,0 +1,96 @@ +//! [`UInt`] bitwise xor operations. + +use super::UInt; +use crate::{Limb, Wrapping}; +use core::ops::{BitXor, BitXorAssign}; +use subtle::{Choice, CtOption}; + +impl UInt { + /// Computes bitwise `a ^ b`. + #[inline(always)] + pub const fn bitxor(&self, rhs: &Self) -> Self { + let mut limbs = [Limb::ZERO; LIMBS]; + let mut i = 0; + + while i < LIMBS { + limbs[i] = self.limbs[i].bitxor(rhs.limbs[i]); + i += 1; + } + + Self { limbs } + } + + /// Perform wrapping bitwise xor. + /// There's no way wrapping could ever happen. + /// This function exists so that all operations are accounted for in the wrapping operations + pub const fn wrapping_xor(&self, rhs: &Self) -> Self { + self.bitxor(rhs) + } + + /// Perform checked bitwise xor, returning a [`CtOption`] which `is_some` always + pub fn checked_xor(&self, rhs: &Self) -> CtOption { + let result = self.bitxor(rhs); + CtOption::new(result, Choice::from(1)) + } +} + +impl BitXor for Wrapping> { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Wrapping> { + Wrapping(self.0.bitxor(&rhs.0)) + } +} + +impl BitXor<&Wrapping>> for Wrapping> { + type Output = Wrapping>; + + fn bitxor(self, rhs: &Wrapping>) -> Wrapping> { + Wrapping(self.0.bitxor(&rhs.0)) + } +} + +impl BitXor>> for &Wrapping> { + type Output = Wrapping>; + + fn bitxor(self, rhs: Wrapping>) -> Wrapping> { + Wrapping(self.0.bitxor(&rhs.0)) + } +} + +impl BitXor<&Wrapping>> for &Wrapping> { + type Output = Wrapping>; + + fn bitxor(self, rhs: &Wrapping>) -> Wrapping> { + Wrapping(self.0.bitxor(&rhs.0)) + } +} + +impl BitXorAssign for Wrapping> { + fn bitxor_assign(&mut self, other: Self) { + *self = *self ^ other; + } +} + +impl BitXorAssign<&Wrapping>> for Wrapping> { + fn bitxor_assign(&mut self, other: &Self) { + *self = *self ^ other; + } +} + +#[cfg(test)] +mod tests { + use crate::U128; + + #[test] + fn checked_xor_ok() { + let result = U128::ZERO.checked_xor(&U128::ONE); + assert_eq!(result.unwrap(), U128::ONE); + } + + #[test] + fn overlapping_xor_ok() { + let result = U128::ZERO.wrapping_xor(&U128::ONE); + assert_eq!(result, U128::ONE); + } +} diff --git a/tests/proptests.rs b/tests/proptests.rs index c423b4d0..bcf11b20 100644 --- a/tests/proptests.rs +++ b/tests/proptests.rs @@ -178,4 +178,15 @@ proptest! { } } + #[test] + fn wrapping_xor(a in uint(), b in uint()) { + let a_bi = to_biguint(&a); + let b_bi = to_biguint(&b); + if !b_bi.is_zero() { + let expected = to_uint(a_bi ^ b_bi); + let actual = a.wrapping_xor(&b); + + assert_eq!(expected, actual); + } + } }