Skip to content

Commit 2988923

Browse files
add multiplication helper and implement exponentiation methods for BoxedUint
Signed-off-by: Andrew Whitehead <cywolf@gmail.com>
1 parent 66a4a06 commit 2988923

7 files changed

Lines changed: 517 additions & 29 deletions

File tree

benches/boxed_uint.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,32 @@ fn bench_mul(c: &mut Criterion) {
108108
BatchSize::SmallInput,
109109
);
110110
});
111+
112+
group.bench_function("boxed_wrapping_pow", |b| {
113+
b.iter_batched(
114+
|| {
115+
(
116+
BoxedUint::random_bits(&mut rng, UINT_BITS),
117+
BoxedUint::random_bits(&mut rng, UINT_BITS),
118+
)
119+
},
120+
|(x, y)| black_box(x.wrapping_pow(&y)),
121+
BatchSize::SmallInput,
122+
);
123+
});
124+
125+
group.bench_function("boxed_wrapping_pow_vartime", |b| {
126+
b.iter_batched(
127+
|| {
128+
(
129+
BoxedUint::random_bits(&mut rng, UINT_BITS),
130+
BoxedUint::random_bits(&mut rng, UINT_BITS),
131+
)
132+
},
133+
|(x, y)| black_box(x.wrapping_pow_vartime(&y)),
134+
BatchSize::SmallInput,
135+
);
136+
});
111137
}
112138

113139
fn bench_division(c: &mut Criterion) {

src/uint/boxed.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod mul;
1818
mod mul_mod;
1919
mod neg;
2020
mod neg_mod;
21+
mod pow;
2122
mod pow_mod;
2223
mod shl;
2324
mod shr;

src/uint/boxed/mul.rs

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,72 @@
11
//! [`BoxedUint`] multiplication operations.
22
33
use crate::{
4-
BoxedUint, CheckedMul, ConcatenatingMul, CtOption, Limb, Mul, MulAssign, Uint, UintRef,
4+
BoxedUint, CheckedMul, Choice, ConcatenatingMul, CtOption, Limb, Mul, MulAssign, Uint, UintRef,
55
Wrapping, WrappingMul,
6-
uint::mul::{karatsuba, wrapping_mul_overflow},
76
};
87

8+
mod helper;
9+
pub(crate) use helper::BoxedMultiplier;
10+
911
impl BoxedUint {
1012
/// Multiply `self` by `rhs`.
1113
///
1214
/// Returns a widened output with a limb count equal to the sums of the input limb counts.
1315
#[must_use]
1416
pub fn mul(&self, rhs: &Self) -> Self {
15-
self.wrapping_mul_carry(rhs.as_limbs(), self.nlimbs() + rhs.nlimbs())
17+
self.wrapping_mul_carry(rhs.as_uint_ref(), self.nlimbs() + rhs.nlimbs())
1618
.0
1719
}
1820

1921
/// Multiply `self` by `rhs`.
2022
///
2123
/// Returns a widened output with a limb count equal to the sums of the input limb counts.
24+
#[must_use]
2225
pub(crate) fn mul_unsigned<const LIMBS: usize>(&self, rhs: &Uint<LIMBS>) -> Self {
23-
self.wrapping_mul_carry(rhs.as_limbs(), self.nlimbs() + LIMBS)
26+
self.wrapping_mul_carry(rhs.as_uint_ref(), self.nlimbs() + LIMBS)
2427
.0
2528
}
2629

2730
/// Perform wrapping multiplication, wrapping to the width of `self`.
2831
#[must_use]
2932
pub fn wrapping_mul(&self, rhs: &Self) -> Self {
30-
self.wrapping_mul_carry(rhs.as_limbs(), self.nlimbs()).0
33+
self.wrapping_mul_carry(rhs.as_uint_ref(), self.nlimbs()).0
3134
}
3235

3336
/// Multiply `self` by `rhs`, wrapping to the width of `self`.
3437
/// Returns `CtOption::None` if the result overflowed the precision of `self`.
3538
#[must_use]
3639
pub fn checked_mul(&self, rhs: &Self) -> CtOption<Self> {
37-
let (res, carry) = self.wrapping_mul_carry(rhs.as_limbs(), self.nlimbs());
38-
let overflow =
39-
wrapping_mul_overflow(self.as_uint_ref(), self.as_uint_ref(), carry.is_nonzero());
40+
let (res, overflow) = self.overflowing_mul(rhs.as_uint_ref(), self.nlimbs());
4041
CtOption::new(res, overflow.not())
4142
}
4243

4344
/// Perform saturating multiplication, returning `MAX` on overflow.
4445
#[must_use]
45-
pub fn saturating_mul(&self, rhs: &BoxedUint) -> Self {
46-
let (mut res, carry) = self.wrapping_mul_carry(rhs.as_limbs(), self.nlimbs());
47-
let overflow =
48-
wrapping_mul_overflow(self.as_uint_ref(), rhs.as_uint_ref(), carry.is_nonzero());
46+
pub fn saturating_mul(&self, rhs: &Self) -> Self {
47+
let (mut res, overflow) = self.overflowing_mul(rhs.as_uint_ref(), self.nlimbs());
4948
res.as_mut_uint_ref().conditional_set_max(overflow);
5049
res
5150
}
5251

52+
/// Multiply `self` by `rhs`, wrapping to the width of `self`.
53+
/// Returns a `Choice` indicating if the result overflowed the precision of `self`.
54+
#[inline(always)]
55+
#[must_use]
56+
pub(crate) fn overflowing_mul(&self, rhs: &UintRef, size: usize) -> (Self, Choice) {
57+
let mut limbs = vec![Limb::ZERO; size];
58+
let overflow = self
59+
.as_uint_ref()
60+
.overflowing_mul(rhs, UintRef::new_mut(&mut limbs));
61+
(limbs.into(), overflow)
62+
}
63+
5364
#[inline(always)]
54-
fn wrapping_mul_carry(&self, rhs: &[Limb], size: usize) -> (Self, Limb) {
65+
fn wrapping_mul_carry(&self, rhs: &UintRef, size: usize) -> (Self, Limb) {
5566
let mut limbs = vec![Limb::ZERO; size];
56-
let carry = karatsuba::wrapping_mul(
57-
self.as_uint_ref(),
58-
UintRef::new(rhs),
59-
UintRef::new_mut(limbs.as_mut_slice()),
60-
false,
61-
);
67+
let carry = self
68+
.as_uint_ref()
69+
.wrapping_mul(rhs, UintRef::new_mut(limbs.as_mut_slice()));
6270
(limbs.into(), carry)
6371
}
6472

@@ -78,29 +86,39 @@ impl BoxedUint {
7886
/// Returns `CtOption::None` if the result overflowed the precision of `self`.
7987
#[must_use]
8088
pub fn checked_square(&self) -> CtOption<Self> {
81-
let (res, carry) = self.wrapping_square_carry(self.nlimbs());
82-
let overflow =
83-
wrapping_mul_overflow(self.as_uint_ref(), self.as_uint_ref(), carry.is_nonzero());
89+
let (res, overflow) = self.overflowing_square(self.nlimbs());
8490
CtOption::new(res, overflow.not())
8591
}
8692

8793
/// Perform saturating squaring, returning `MAX` on overflow.
8894
#[must_use]
8995
pub fn saturating_square(&self) -> Self {
90-
let (mut res, carry) = self.wrapping_square_carry(self.nlimbs());
91-
let overflow =
92-
wrapping_mul_overflow(self.as_uint_ref(), self.as_uint_ref(), carry.is_nonzero());
96+
let (mut res, overflow) = self.overflowing_square(self.nlimbs());
9397
res.as_mut_uint_ref().conditional_set_max(overflow);
9498
res
9599
}
96100

101+
/// Multiply `self` by itself, wrapping to the width of `self`.
102+
/// Returns a `Choice` indicating if the result overflowed the precision of `self`.
103+
#[inline(always)]
104+
#[must_use]
105+
pub(crate) fn overflowing_square(&self, size: usize) -> (Self, Choice) {
106+
let mut limbs = vec![Limb::ZERO; size];
107+
let overflow = self
108+
.as_uint_ref()
109+
.overflowing_square(UintRef::new_mut(&mut limbs));
110+
(limbs.into(), overflow)
111+
}
112+
97113
/// Multiply `self` by itself, wrapping to the width of `self`.
98114
/// Returns a pair of the wrapped product and a Limb representing the carry.
99115
#[inline(always)]
116+
#[must_use]
100117
fn wrapping_square_carry(&self, size: usize) -> (Self, Limb) {
101118
let mut limbs = vec![Limb::ZERO; size];
102-
let carry =
103-
karatsuba::wrapping_square(self.as_uint_ref(), UintRef::new_mut(limbs.as_mut_slice()));
119+
let carry = self
120+
.as_uint_ref()
121+
.wrapping_square(UintRef::new_mut(limbs.as_mut_slice()));
104122
(limbs.into(), carry)
105123
}
106124
}
@@ -152,7 +170,7 @@ impl MulAssign<BoxedUint> for BoxedUint {
152170

153171
impl MulAssign<&BoxedUint> for BoxedUint {
154172
fn mul_assign(&mut self, rhs: &BoxedUint) {
155-
*self = self.clone().mul(rhs);
173+
*self = BoxedUint::mul(self, rhs);
156174
}
157175
}
158176

src/uint/boxed/mul/helper.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//! [`BoxedUint`] multiplication helper to reduce allocations.
2+
3+
use alloc::vec::Vec;
4+
5+
use crate::{BoxedUint, Choice, Limb, UintRef};
6+
7+
/// Boxed multiplier with a pre-allocated internal buffer to avoid additional allocations.
8+
#[derive(Debug, Clone)]
9+
pub struct BoxedMultiplier {
10+
product: Vec<Limb>,
11+
}
12+
13+
impl BoxedMultiplier {
14+
pub fn new() -> Self {
15+
Self {
16+
product: Vec::new(),
17+
}
18+
}
19+
20+
fn get_buffer(&mut self, limbs: usize) -> &mut UintRef {
21+
self.product.clear();
22+
self.product.resize(limbs, Limb::ZERO);
23+
UintRef::new_mut(&mut self.product[..limbs])
24+
}
25+
26+
pub fn overflowing_mul(&mut self, lhs: &BoxedUint, rhs: &BoxedUint) -> (&mut UintRef, Choice) {
27+
let buf = self.get_buffer(lhs.nlimbs());
28+
let overflow = lhs.as_uint_ref().overflowing_mul(rhs.as_uint_ref(), buf);
29+
(buf, overflow)
30+
}
31+
32+
pub fn overflowing_mul_assign(&mut self, lhs: &mut BoxedUint, rhs: &BoxedUint) -> Choice {
33+
let (buf, overflow) = self.overflowing_mul(lhs, rhs);
34+
lhs.as_mut_uint_ref().copy_from(buf);
35+
overflow
36+
}
37+
38+
pub fn overflowing_square(&mut self, lhs: &BoxedUint) -> (&mut UintRef, Choice) {
39+
let buf = self.get_buffer(lhs.nlimbs());
40+
let overflow = lhs.as_uint_ref().overflowing_square(buf);
41+
(buf, overflow)
42+
}
43+
44+
pub fn overflowing_square_assign(&mut self, lhs: &mut BoxedUint) -> Choice {
45+
let (buf, overflow) = self.overflowing_square(lhs);
46+
lhs.as_mut_uint_ref().copy_from(buf);
47+
overflow
48+
}
49+
50+
pub fn wrapping_mul(&mut self, lhs: &BoxedUint, rhs: &BoxedUint) -> &mut UintRef {
51+
self.wrapping_mul_with_carry(lhs, rhs).0
52+
}
53+
54+
pub fn wrapping_mul_assign(&mut self, lhs: &mut BoxedUint, rhs: &BoxedUint) {
55+
let (buf, _) = self.wrapping_mul_with_carry(lhs, rhs);
56+
lhs.as_mut_uint_ref().copy_from(buf);
57+
}
58+
59+
fn wrapping_mul_with_carry(
60+
&mut self,
61+
lhs: &BoxedUint,
62+
rhs: &BoxedUint,
63+
) -> (&mut UintRef, Limb) {
64+
let buf = self.get_buffer(lhs.nlimbs());
65+
let carry = lhs.as_uint_ref().wrapping_mul(rhs.as_uint_ref(), buf);
66+
(buf, carry)
67+
}
68+
69+
pub fn wrapping_square_assign(&mut self, lhs: &mut BoxedUint) {
70+
let (buf, _) = self.wrapping_square_with_carry(lhs);
71+
lhs.as_mut_uint_ref().copy_from(buf);
72+
}
73+
74+
fn wrapping_square_with_carry(&mut self, lhs: &BoxedUint) -> (&mut UintRef, Limb) {
75+
let buf = self.get_buffer(lhs.nlimbs());
76+
let carry = lhs.as_uint_ref().wrapping_square(buf);
77+
(buf, carry)
78+
}
79+
}

0 commit comments

Comments
 (0)