Skip to content
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
21 changes: 21 additions & 0 deletions benches/boxed_uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,34 @@ fn bench_mul(c: &mut Criterion) {
)
});

group.bench_function("boxed_wrapping_mul", |b| {
b.iter_batched(
|| {
(
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
BoxedUint::random_bits(&mut OsRng, UINT_BITS),
)
},
|(x, y)| black_box(x.wrapping_mul(&y)),
BatchSize::SmallInput,
)
});

group.bench_function("boxed_square", |b| {
b.iter_batched(
|| BoxedUint::random_bits(&mut OsRng, UINT_BITS),
|x| black_box(x.square()),
BatchSize::SmallInput,
)
});

group.bench_function("boxed_wrapping_square", |b| {
b.iter_batched(
|| BoxedUint::random_bits(&mut OsRng, UINT_BITS),
|x| black_box(x.wrapping_square()),
BatchSize::SmallInput,
)
});
}

fn bench_division(c: &mut Criterion) {
Expand Down
50 changes: 49 additions & 1 deletion benches/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use criterion::{
};
use crypto_bigint::{
Gcd, Limb, NonZero, Odd, OddUint, Random, RandomBits, RandomMod, Reciprocal, U128, U256, U512,
U1024, U2048, U4096, Uint,
U1024, U2048, U4096, U8192, Uint,
};
use rand_chacha::ChaCha8Rng;
use rand_core::{RngCore, SeedableRng};
Expand Down Expand Up @@ -166,6 +166,38 @@ fn bench_mul(c: &mut Criterion) {
)
});

group.bench_function("widening_mul, U8192xU4096", |b| {
b.iter_batched(
|| (U8192::random(&mut rng), U4096::random(&mut rng)),
|(x, y)| black_box(x.widening_mul(&y)),
BatchSize::SmallInput,
)
});

group.bench_function("wrapping_mul, U256xU256", |b| {
b.iter_batched(
|| (U256::random(&mut rng), U256::random(&mut rng)),
|(x, y)| black_box(x.wrapping_mul(&y)),
BatchSize::SmallInput,
)
});

group.bench_function("wrapping_mul, U4096xU4096", |b| {
b.iter_batched(
|| (U4096::random(&mut rng), U4096::random(&mut rng)),
|(x, y)| black_box(x.wrapping_mul(&y)),
BatchSize::SmallInput,
)
});

group.bench_function("wrapping_mul, U8192xU4096", |b| {
b.iter_batched(
|| (U8192::random(&mut rng), U4096::random(&mut rng)),
|(x, y)| black_box(x.wrapping_mul(&y)),
BatchSize::SmallInput,
)
});

group.bench_function("square_wide, U256", |b| {
b.iter_batched(
|| U256::random(&mut rng),
Expand All @@ -182,6 +214,22 @@ fn bench_mul(c: &mut Criterion) {
)
});

group.bench_function("wrapping_square, U256xU256", |b| {
b.iter_batched(
|| U256::random(&mut rng),
|x| x.wrapping_square(),
BatchSize::SmallInput,
)
});

group.bench_function("wrapping_square, U4096xU4096", |b| {
b.iter_batched(
|| (U4096::random(&mut rng)),
|x| x.wrapping_square(),
BatchSize::SmallInput,
)
});

group.bench_function("mul_mod, U256", |b| {
b.iter_batched(
|| {
Expand Down
48 changes: 43 additions & 5 deletions src/uint/boxed/mul.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! [`BoxedUint`] multiplication operations.

use crate::{
BoxedUint, CheckedMul, ConcatenatingMul, Limb, Resize, Uint, Wrapping, WrappingMul, Zero,
BoxedUint, CheckedMul, ConcatenatingMul, Limb, Uint, Wrapping, WrappingMul, Zero,
uint::mul::{
karatsuba::{KARATSUBA_MIN_STARTING_LIMBS, karatsuba_mul_limbs, karatsuba_square_limbs},
mul_limbs, square_limbs,
mul_limbs, schoolbook, square_limbs,
},
};
use core::ops::{Mul, MulAssign};
Expand Down Expand Up @@ -45,7 +45,26 @@ impl BoxedUint {

/// Perform wrapping multiplication, wrapping to the width of `self`.
pub fn wrapping_mul(&self, rhs: &Self) -> Self {
self.mul(rhs).resize_unchecked(self.bits_precision())
self.wrapping_mul_limbs(rhs.as_limbs())
}

#[inline(always)]
fn wrapping_mul_limbs(&self, rhs: &[Limb]) -> Self {
// Perform a widening Karatsuba multiplication and truncate
// for very large numbers, where the performance is better.
if self.nlimbs().min(rhs.len()) > 200 {
let size = self.nlimbs() + rhs.len();
let overlap = self.nlimbs().min(rhs.len());
let mut limbs = vec![Limb::ZERO; size + overlap * 2];
let (out, scratch) = limbs.as_mut_slice().split_at_mut(size);
karatsuba_mul_limbs(&self.limbs, rhs, out, scratch);
limbs.truncate(self.nlimbs());
return limbs.into();
}

let mut limbs = vec![Limb::ZERO; self.nlimbs()];
schoolbook::wrapping_mul(&self.limbs, rhs, &mut limbs);
limbs.into()
}

/// Multiply `self` by itself.
Expand All @@ -64,6 +83,13 @@ impl BoxedUint {
square_limbs(&self.limbs, &mut limbs);
limbs.into()
}

/// Multiply `self` by itself, wrapping to the width of `self`.
pub fn wrapping_square(&self) -> Self {
let mut limbs = vec![Limb::ZERO; self.nlimbs()];
schoolbook::wrapping_square(&self.limbs, &mut limbs);
limbs.into()
}
}

impl CheckedMul for BoxedUint {
Expand Down Expand Up @@ -196,19 +222,31 @@ mod tests {
#[cfg(feature = "rand_core")]
#[test]
fn mul_cmp() {
use crate::RandomBits;
use crate::{RandomBits, Resize};
use rand_core::SeedableRng;
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1);

for _ in 0..50 {
let a = BoxedUint::random_bits(&mut rng, 4096);
assert_eq!(a.mul(&a), a.square(), "a = {a}");
assert_eq!(a.wrapping_mul(&a), a.wrapping_square(), "a = {a}");
}

for _ in 0..50 {
let a = BoxedUint::random_bits(&mut rng, 4096);
let b = BoxedUint::random_bits(&mut rng, 5000);
assert_eq!(a.mul(&b), b.mul(&a), "a={a}, b={b}");
let expect = a.mul(&b);
assert_eq!(b.mul(&a), expect, "a={a}, b={b}");
assert_eq!(
a.wrapping_mul(&b),
expect.clone().resize_unchecked(a.bits_precision()),
"a={a}, b={b}"
);
assert_eq!(
b.wrapping_mul(&a),
expect.clone().resize_unchecked(b.bits_precision()),
"a={a}, b={b}"
);
}
}
}
Loading