Skip to content

Commit 986faf2

Browse files
authored
Add Int bit ops (#697)
1 parent 04079c8 commit 986faf2

11 files changed

Lines changed: 901 additions & 4 deletions

File tree

src/const_choice.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use subtle::{Choice, CtOption};
22

3-
use crate::{modular::SafeGcdInverter, Limb, NonZero, Odd, Uint, WideWord, Word};
3+
use crate::{modular::SafeGcdInverter, Int, Limb, NonZero, Odd, Uint, WideWord, Word};
44

55
/// A boolean value returned by constant-time `const fn`s.
66
// TODO: should be replaced by `subtle::Choice` or `CtOption`
@@ -391,6 +391,12 @@ impl<const LIMBS: usize> ConstCtOption<Uint<LIMBS>> {
391391
assert!(self.is_some.is_true_vartime(), "{}", msg);
392392
self.value
393393
}
394+
395+
/// Returns the contained value, interpreting the underlying [`Uint`] value as an [`Int`].
396+
#[inline]
397+
pub const fn as_int(&self) -> ConstCtOption<Int<LIMBS>> {
398+
ConstCtOption::new(Int::from_bits(self.value), self.is_some)
399+
}
394400
}
395401

396402
impl<const LIMBS: usize> ConstCtOption<(Uint<LIMBS>, Uint<LIMBS>)> {
@@ -435,6 +441,26 @@ impl<const LIMBS: usize> ConstCtOption<Odd<Uint<LIMBS>>> {
435441
}
436442
}
437443

444+
impl<const LIMBS: usize> ConstCtOption<Int<LIMBS>> {
445+
/// This returns the underlying value if it is `Some` or the provided value otherwise.
446+
#[inline]
447+
pub const fn unwrap_or(self, def: Int<LIMBS>) -> Int<LIMBS> {
448+
Int::select(&def, &self.value, self.is_some)
449+
}
450+
451+
/// Returns the contained value, consuming the `self` value.
452+
///
453+
/// # Panics
454+
///
455+
/// Panics if the value is none with a custom panic message provided by
456+
/// `msg`.
457+
#[inline]
458+
pub const fn expect(self, msg: &str) -> Int<LIMBS> {
459+
assert!(self.is_some.is_true_vartime(), "{}", msg);
460+
self.value
461+
}
462+
}
463+
438464
impl ConstCtOption<NonZero<Limb>> {
439465
/// Returns the contained value, consuming the `self` value.
440466
///

src/int.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@ use crate::Encoding;
1212
use crate::{Bounded, ConstChoice, ConstCtOption, Constants, Limb, NonZero, Odd, Uint, Word};
1313

1414
mod add;
15+
mod bit_and;
16+
mod bit_not;
17+
mod bit_or;
18+
mod bit_xor;
1519
mod cmp;
1620
mod div;
1721
mod encoding;
1822
mod from;
1923
mod mul;
2024
mod neg;
2125
mod resize;
26+
mod shl;
27+
mod shr;
2228
mod sign;
2329
mod sub;
2430
pub(crate) mod types;

src/int/bit_and.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//! [`Int`] bitwise AND operations.
2+
3+
use core::ops::{BitAnd, BitAndAssign};
4+
5+
use crate::{ConstCtOption, Int, Limb, Uint, Wrapping};
6+
7+
impl<const LIMBS: usize> Int<LIMBS> {
8+
/// Computes bitwise `a & b`.
9+
#[inline(always)]
10+
pub const fn bitand(&self, rhs: &Self) -> Self {
11+
Self(Uint::bitand(&self.0, &rhs.0))
12+
}
13+
14+
/// Perform bitwise `AND` between `self` and the given [`Limb`], performing the `AND` operation
15+
/// on every limb of `self`.
16+
pub const fn bitand_limb(&self, rhs: Limb) -> Self {
17+
Self(Uint::bitand_limb(&self.0, rhs))
18+
}
19+
20+
/// Perform wrapping bitwise `AND`.
21+
///
22+
/// There's no way wrapping could ever happen.
23+
/// This function exists so that all operations are accounted for in the wrapping operations
24+
pub const fn wrapping_and(&self, rhs: &Self) -> Self {
25+
self.bitand(rhs)
26+
}
27+
28+
/// Perform checked bitwise `AND`, returning a [`ConstCtOption`] which `is_some` always
29+
pub const fn checked_and(&self, rhs: &Self) -> ConstCtOption<Self> {
30+
ConstCtOption::some(self.bitand(rhs))
31+
}
32+
}
33+
34+
impl<const LIMBS: usize> BitAnd for Int<LIMBS> {
35+
type Output = Self;
36+
37+
fn bitand(self, rhs: Self) -> Int<LIMBS> {
38+
self.bitand(&rhs)
39+
}
40+
}
41+
42+
impl<const LIMBS: usize> BitAnd<&Int<LIMBS>> for Int<LIMBS> {
43+
type Output = Int<LIMBS>;
44+
45+
#[allow(clippy::needless_borrow)]
46+
fn bitand(self, rhs: &Int<LIMBS>) -> Int<LIMBS> {
47+
(&self).bitand(rhs)
48+
}
49+
}
50+
51+
impl<const LIMBS: usize> BitAnd<Int<LIMBS>> for &Int<LIMBS> {
52+
type Output = Int<LIMBS>;
53+
54+
fn bitand(self, rhs: Int<LIMBS>) -> Int<LIMBS> {
55+
self.bitand(&rhs)
56+
}
57+
}
58+
59+
impl<const LIMBS: usize> BitAnd<&Int<LIMBS>> for &Int<LIMBS> {
60+
type Output = Int<LIMBS>;
61+
62+
fn bitand(self, rhs: &Int<LIMBS>) -> Int<LIMBS> {
63+
self.bitand(rhs)
64+
}
65+
}
66+
67+
impl<const LIMBS: usize> BitAndAssign for Int<LIMBS> {
68+
#[allow(clippy::assign_op_pattern)]
69+
fn bitand_assign(&mut self, other: Self) {
70+
*self = *self & other;
71+
}
72+
}
73+
74+
impl<const LIMBS: usize> BitAndAssign<&Int<LIMBS>> for Int<LIMBS> {
75+
#[allow(clippy::assign_op_pattern)]
76+
fn bitand_assign(&mut self, other: &Self) {
77+
*self = *self & other;
78+
}
79+
}
80+
81+
impl<const LIMBS: usize> BitAnd for Wrapping<Int<LIMBS>> {
82+
type Output = Self;
83+
84+
fn bitand(self, rhs: Self) -> Wrapping<Int<LIMBS>> {
85+
Wrapping(self.0.bitand(&rhs.0))
86+
}
87+
}
88+
89+
impl<const LIMBS: usize> BitAnd<&Wrapping<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
90+
type Output = Wrapping<Int<LIMBS>>;
91+
92+
fn bitand(self, rhs: &Wrapping<Int<LIMBS>>) -> Wrapping<Int<LIMBS>> {
93+
Wrapping(self.0.bitand(&rhs.0))
94+
}
95+
}
96+
97+
impl<const LIMBS: usize> BitAnd<Wrapping<Int<LIMBS>>> for &Wrapping<Int<LIMBS>> {
98+
type Output = Wrapping<Int<LIMBS>>;
99+
100+
fn bitand(self, rhs: Wrapping<Int<LIMBS>>) -> Wrapping<Int<LIMBS>> {
101+
Wrapping(self.0.bitand(&rhs.0))
102+
}
103+
}
104+
105+
impl<const LIMBS: usize> BitAnd<&Wrapping<Int<LIMBS>>> for &Wrapping<Int<LIMBS>> {
106+
type Output = Wrapping<Int<LIMBS>>;
107+
108+
fn bitand(self, rhs: &Wrapping<Int<LIMBS>>) -> Wrapping<Int<LIMBS>> {
109+
Wrapping(self.0.bitand(&rhs.0))
110+
}
111+
}
112+
113+
impl<const LIMBS: usize> BitAndAssign for Wrapping<Int<LIMBS>> {
114+
#[allow(clippy::assign_op_pattern)]
115+
fn bitand_assign(&mut self, other: Self) {
116+
*self = *self & other;
117+
}
118+
}
119+
120+
impl<const LIMBS: usize> BitAndAssign<&Wrapping<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
121+
#[allow(clippy::assign_op_pattern)]
122+
fn bitand_assign(&mut self, other: &Self) {
123+
*self = *self & other;
124+
}
125+
}
126+
127+
#[cfg(test)]
128+
mod tests {
129+
use crate::I128;
130+
131+
#[test]
132+
fn checked_and_ok() {
133+
assert_eq!(I128::ZERO.checked_and(&I128::ONE).unwrap(), I128::ZERO);
134+
}
135+
136+
#[test]
137+
fn overlapping_and_ok() {
138+
assert_eq!(I128::MAX.wrapping_and(&I128::ONE), I128::ONE);
139+
}
140+
}

src/int/bit_not.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//! [`Int`] bitwise NOT operations.
2+
3+
use core::ops::Not;
4+
5+
use crate::{Uint, Wrapping};
6+
7+
use super::Int;
8+
9+
impl<const LIMBS: usize> Int<LIMBS> {
10+
/// Computes bitwise `!a`.
11+
#[inline(always)]
12+
pub const fn not(&self) -> Self {
13+
Self(Uint::not(&self.0))
14+
}
15+
}
16+
17+
impl<const LIMBS: usize> Not for Int<LIMBS> {
18+
type Output = Self;
19+
20+
fn not(self) -> Self {
21+
Self::not(&self)
22+
}
23+
}
24+
25+
impl<const LIMBS: usize> Not for Wrapping<Int<LIMBS>> {
26+
type Output = Self;
27+
28+
fn not(self) -> <Self as Not>::Output {
29+
Wrapping(self.0.not())
30+
}
31+
}
32+
33+
#[cfg(test)]
34+
mod tests {
35+
use crate::I128;
36+
37+
#[test]
38+
fn bitnot_ok() {
39+
assert_eq!(I128::ZERO.not(), I128::MINUS_ONE);
40+
assert_eq!(I128::MAX.not(), I128::MIN);
41+
}
42+
}

src/int/bit_or.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//! [`Int`] bitwise OR operations.
2+
3+
use core::ops::{BitOr, BitOrAssign};
4+
5+
use crate::{ConstCtOption, Uint, Wrapping};
6+
7+
use super::Int;
8+
9+
impl<const LIMBS: usize> Int<LIMBS> {
10+
/// Computes bitwise `a & b`.
11+
#[inline(always)]
12+
pub const fn bitor(&self, rhs: &Self) -> Self {
13+
Self(Uint::bitor(&self.0, &rhs.0))
14+
}
15+
16+
/// Perform wrapping bitwise `OR`.
17+
///
18+
/// There's no way wrapping could ever happen.
19+
/// This function exists so that all operations are accounted for in the wrapping operations
20+
pub const fn wrapping_or(&self, rhs: &Self) -> Self {
21+
self.bitor(rhs)
22+
}
23+
24+
/// Perform checked bitwise `OR`, returning a [`ConstCtOption`] which `is_some` always
25+
pub const fn checked_or(&self, rhs: &Self) -> ConstCtOption<Self> {
26+
ConstCtOption::some(self.bitor(rhs))
27+
}
28+
}
29+
30+
impl<const LIMBS: usize> BitOr for Int<LIMBS> {
31+
type Output = Self;
32+
33+
fn bitor(self, rhs: Self) -> Int<LIMBS> {
34+
self.bitor(&rhs)
35+
}
36+
}
37+
38+
impl<const LIMBS: usize> BitOr<&Int<LIMBS>> for Int<LIMBS> {
39+
type Output = Int<LIMBS>;
40+
41+
#[allow(clippy::needless_borrow)]
42+
fn bitor(self, rhs: &Int<LIMBS>) -> Int<LIMBS> {
43+
(&self).bitor(rhs)
44+
}
45+
}
46+
47+
impl<const LIMBS: usize> BitOr<Int<LIMBS>> for &Int<LIMBS> {
48+
type Output = Int<LIMBS>;
49+
50+
fn bitor(self, rhs: Int<LIMBS>) -> Int<LIMBS> {
51+
self.bitor(&rhs)
52+
}
53+
}
54+
55+
impl<const LIMBS: usize> BitOr<&Int<LIMBS>> for &Int<LIMBS> {
56+
type Output = Int<LIMBS>;
57+
58+
fn bitor(self, rhs: &Int<LIMBS>) -> Int<LIMBS> {
59+
self.bitor(rhs)
60+
}
61+
}
62+
63+
impl<const LIMBS: usize> BitOrAssign for Int<LIMBS> {
64+
fn bitor_assign(&mut self, other: Self) {
65+
*self = *self | other;
66+
}
67+
}
68+
69+
impl<const LIMBS: usize> BitOrAssign<&Int<LIMBS>> for Int<LIMBS> {
70+
fn bitor_assign(&mut self, other: &Self) {
71+
*self = *self | other;
72+
}
73+
}
74+
75+
impl<const LIMBS: usize> BitOr for Wrapping<Int<LIMBS>> {
76+
type Output = Self;
77+
78+
fn bitor(self, rhs: Self) -> Wrapping<Int<LIMBS>> {
79+
Wrapping(self.0.bitor(&rhs.0))
80+
}
81+
}
82+
83+
impl<const LIMBS: usize> BitOr<&Wrapping<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
84+
type Output = Wrapping<Int<LIMBS>>;
85+
86+
fn bitor(self, rhs: &Wrapping<Int<LIMBS>>) -> Wrapping<Int<LIMBS>> {
87+
Wrapping(self.0.bitor(&rhs.0))
88+
}
89+
}
90+
91+
impl<const LIMBS: usize> BitOr<Wrapping<Int<LIMBS>>> for &Wrapping<Int<LIMBS>> {
92+
type Output = Wrapping<Int<LIMBS>>;
93+
94+
fn bitor(self, rhs: Wrapping<Int<LIMBS>>) -> Wrapping<Int<LIMBS>> {
95+
Wrapping(self.0.bitor(&rhs.0))
96+
}
97+
}
98+
99+
impl<const LIMBS: usize> BitOr<&Wrapping<Int<LIMBS>>> for &Wrapping<Int<LIMBS>> {
100+
type Output = Wrapping<Int<LIMBS>>;
101+
102+
fn bitor(self, rhs: &Wrapping<Int<LIMBS>>) -> Wrapping<Int<LIMBS>> {
103+
Wrapping(self.0.bitor(&rhs.0))
104+
}
105+
}
106+
107+
impl<const LIMBS: usize> BitOrAssign for Wrapping<Int<LIMBS>> {
108+
fn bitor_assign(&mut self, other: Self) {
109+
*self = *self | other;
110+
}
111+
}
112+
113+
impl<const LIMBS: usize> BitOrAssign<&Wrapping<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
114+
fn bitor_assign(&mut self, other: &Self) {
115+
*self = *self | other;
116+
}
117+
}
118+
119+
#[cfg(test)]
120+
mod tests {
121+
use crate::I128;
122+
123+
#[test]
124+
fn checked_or_ok() {
125+
assert_eq!(I128::ZERO.checked_or(&I128::ONE).unwrap(), I128::ONE);
126+
}
127+
128+
#[test]
129+
fn overlapping_or_ok() {
130+
assert_eq!(I128::MAX.wrapping_or(&I128::ONE), I128::MAX);
131+
}
132+
}

0 commit comments

Comments
 (0)