Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bitwise xor and not #27

Merged
merged 5 commits into from
Oct 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/limb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

mod add;
mod bit_and;
mod bit_not;
mod bit_or;
mod bit_xor;
mod cmp;
mod encoding;
mod from;
Expand Down
19 changes: 19 additions & 0 deletions src/limb/bit_not.rs
Original file line number Diff line number Diff line change
@@ -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) -> <Self as Not>::Output {
self.not()
}
}
19 changes: 19 additions & 0 deletions src/limb/bit_xor.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
6 changes: 4 additions & 2 deletions src/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
File renamed without changes.
40 changes: 40 additions & 0 deletions src/uint/bit_not.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//! [`UInt`] bitwise not operations.

use super::UInt;
use crate::{Limb, Wrapping};
use core::ops::Not;

impl<const LIMBS: usize> UInt<LIMBS> {
/// 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<const LIMBS: usize> Not for Wrapping<UInt<LIMBS>> {
type Output = Self;

fn not(self) -> <Self as Not>::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);
}
}
File renamed without changes.
96 changes: 96 additions & 0 deletions src/uint/bit_xor.rs
Original file line number Diff line number Diff line change
@@ -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<const LIMBS: usize> UInt<LIMBS> {
/// 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<Self> {
let result = self.bitxor(rhs);
CtOption::new(result, Choice::from(1))
}
}

impl<const LIMBS: usize> BitXor for Wrapping<UInt<LIMBS>> {
type Output = Self;

fn bitxor(self, rhs: Self) -> Wrapping<UInt<LIMBS>> {
Wrapping(self.0.bitxor(&rhs.0))
}
}

impl<const LIMBS: usize> BitXor<&Wrapping<UInt<LIMBS>>> for Wrapping<UInt<LIMBS>> {
type Output = Wrapping<UInt<LIMBS>>;

fn bitxor(self, rhs: &Wrapping<UInt<LIMBS>>) -> Wrapping<UInt<LIMBS>> {
Wrapping(self.0.bitxor(&rhs.0))
}
}

impl<const LIMBS: usize> BitXor<Wrapping<UInt<LIMBS>>> for &Wrapping<UInt<LIMBS>> {
type Output = Wrapping<UInt<LIMBS>>;

fn bitxor(self, rhs: Wrapping<UInt<LIMBS>>) -> Wrapping<UInt<LIMBS>> {
Wrapping(self.0.bitxor(&rhs.0))
}
}

impl<const LIMBS: usize> BitXor<&Wrapping<UInt<LIMBS>>> for &Wrapping<UInt<LIMBS>> {
type Output = Wrapping<UInt<LIMBS>>;

fn bitxor(self, rhs: &Wrapping<UInt<LIMBS>>) -> Wrapping<UInt<LIMBS>> {
Wrapping(self.0.bitxor(&rhs.0))
}
}

impl<const LIMBS: usize> BitXorAssign for Wrapping<UInt<LIMBS>> {
fn bitxor_assign(&mut self, other: Self) {
*self = *self ^ other;
}
}

impl<const LIMBS: usize> BitXorAssign<&Wrapping<UInt<LIMBS>>> for Wrapping<UInt<LIMBS>> {
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);
}
}
11 changes: 11 additions & 0 deletions tests/proptests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}