1use crate::{
4 Checked, CheckedMul, Choice, Concat, ConcatenatingMul, ConcatenatingSquare, CtOption, Limb,
5 Mul, MulAssign, Uint, Wrapping, WrappingMul,
6};
7
8pub(crate) mod karatsuba;
9pub(crate) mod schoolbook;
10
11impl<const LIMBS: usize> Uint<LIMBS> {
12 #[must_use]
14 pub const fn concatenating_mul<const RHS_LIMBS: usize, const WIDE_LIMBS: usize>(
15 &self,
16 rhs: &Uint<RHS_LIMBS>,
17 ) -> Uint<WIDE_LIMBS>
18 where
19 Self: Concat<RHS_LIMBS, Output = Uint<WIDE_LIMBS>>,
20 {
21 let (lo, hi) = self.widening_mul(rhs);
22 Uint::concat_mixed(&lo, &hi)
23 }
24
25 #[deprecated(since = "0.7.0", note = "please use `widening_mul` instead")]
28 #[must_use]
29 pub const fn split_mul<const RHS_LIMBS: usize>(
30 &self,
31 rhs: &Uint<RHS_LIMBS>,
32 ) -> (Self, Uint<RHS_LIMBS>) {
33 self.widening_mul(rhs)
34 }
35
36 #[inline(always)]
39 #[must_use]
40 pub const fn widening_mul<const RHS_LIMBS: usize>(
41 &self,
42 rhs: &Uint<RHS_LIMBS>,
43 ) -> (Self, Uint<RHS_LIMBS>) {
44 karatsuba::widening_mul_fixed(self.as_uint_ref(), rhs.as_uint_ref())
45 }
46
47 #[inline]
49 #[must_use]
50 pub const fn wrapping_mul<const RHS_LIMBS: usize>(&self, rhs: &Uint<RHS_LIMBS>) -> Self {
51 karatsuba::wrapping_mul_fixed::<LIMBS>(self.as_uint_ref(), rhs.as_uint_ref()).0
52 }
53
54 #[must_use]
56 pub const fn saturating_mul<const RHS_LIMBS: usize>(&self, rhs: &Uint<RHS_LIMBS>) -> Self {
57 let (lo, overflow) = self.overflowing_mul(rhs);
58 Self::select(&lo, &Self::MAX, overflow)
59 }
60
61 #[must_use]
63 pub const fn checked_mul<const RHS_LIMBS: usize>(
64 &self,
65 rhs: &Uint<RHS_LIMBS>,
66 ) -> CtOption<Uint<LIMBS>> {
67 let (lo, overflow) = self.overflowing_mul(rhs);
68 CtOption::new(lo, overflow.not())
69 }
70
71 #[inline(always)]
74 #[must_use]
75 pub(crate) const fn overflowing_mul<const RHS_LIMBS: usize>(
76 &self,
77 rhs: &Uint<RHS_LIMBS>,
78 ) -> (Uint<LIMBS>, Choice) {
79 let (lo, carry) = karatsuba::wrapping_mul_fixed(self.as_uint_ref(), rhs.as_uint_ref());
80 let overflow = self
81 .as_uint_ref()
82 .check_mul_overflow(rhs.as_uint_ref(), carry.is_nonzero());
83 (lo, overflow)
84 }
85
86 #[inline]
88 pub(crate) const fn overflowing_mul_limb(&self, rhs: Limb) -> (Self, Limb) {
89 let mut ret = [Limb::ZERO; LIMBS];
90 let mut i = 0;
91 let mut carry = Limb::ZERO;
92 while i < LIMBS {
93 (ret[i], carry) = self.limbs[i].carrying_mul_add(rhs, Limb::ZERO, carry);
94 i += 1;
95 }
96 (Uint::new(ret), carry)
97 }
98
99 #[inline]
101 pub(crate) const fn wrapping_mul_limb(&self, rhs: Limb) -> Self {
102 self.overflowing_mul_limb(rhs).0
103 }
104}
105
106impl<const LIMBS: usize> Uint<LIMBS> {
108 #[inline(always)]
110 #[must_use]
111 #[deprecated(since = "0.7.0", note = "please use `widening_square` instead")]
112 pub const fn square_wide(&self) -> (Self, Self) {
113 self.widening_square()
114 }
115
116 #[inline(always)]
118 #[must_use]
119 pub const fn widening_square(&self) -> (Self, Self) {
120 karatsuba::widening_square_fixed(self.as_uint_ref())
121 }
122
123 #[must_use]
125 pub const fn concatenating_square<const WIDE_LIMBS: usize>(&self) -> Uint<WIDE_LIMBS>
126 where
127 Self: Concat<LIMBS, Output = Uint<WIDE_LIMBS>>,
128 {
129 let (lo, hi) = self.widening_square();
130 Uint::concat_mixed(&lo, &hi)
131 }
132
133 #[must_use]
135 pub const fn checked_square(&self) -> CtOption<Uint<LIMBS>> {
136 let (lo, overflow) = self.overflowing_square();
137 CtOption::new(lo, overflow.not())
138 }
139
140 #[inline]
142 #[must_use]
143 pub const fn wrapping_square(&self) -> Uint<LIMBS> {
144 karatsuba::wrapping_square_fixed(self.as_uint_ref()).0
145 }
146
147 #[must_use]
149 pub const fn saturating_square(&self) -> Self {
150 let (lo, overflow) = self.overflowing_square();
151 Self::select(&lo, &Self::MAX, overflow)
152 }
153
154 #[inline(always)]
157 #[must_use]
158 pub(crate) const fn overflowing_square(&self) -> (Uint<LIMBS>, Choice) {
159 let (lo, carry) = karatsuba::wrapping_square_fixed(self.as_uint_ref());
160 let overflow = self.as_uint_ref().check_square_overflow(carry.is_nonzero());
161 (lo, overflow)
162 }
163}
164
165impl<const LIMBS: usize, const WIDE_LIMBS: usize> Uint<LIMBS>
166where
167 Self: Concat<LIMBS, Output = Uint<WIDE_LIMBS>>,
168{
169 #[must_use]
171 #[deprecated(since = "0.7.0", note = "please use `concatenating_square` instead")]
172 pub const fn square(&self) -> Uint<WIDE_LIMBS> {
173 let (lo, hi) = self.widening_square();
174 lo.concat(&hi)
175 }
176}
177
178impl<const LIMBS: usize, const RHS_LIMBS: usize> CheckedMul<Uint<RHS_LIMBS>> for Uint<LIMBS> {
179 fn checked_mul(&self, rhs: &Uint<RHS_LIMBS>) -> CtOption<Self> {
180 self.checked_mul(rhs)
181 }
182}
183
184impl<const LIMBS: usize, const RHS_LIMBS: usize> Mul<Uint<RHS_LIMBS>> for Uint<LIMBS> {
185 type Output = Uint<LIMBS>;
186
187 fn mul(self, rhs: Uint<RHS_LIMBS>) -> Self {
188 self.mul(&rhs)
189 }
190}
191
192impl<const LIMBS: usize, const RHS_LIMBS: usize> Mul<&Uint<RHS_LIMBS>> for Uint<LIMBS> {
193 type Output = Uint<LIMBS>;
194
195 fn mul(self, rhs: &Uint<RHS_LIMBS>) -> Self {
196 (&self).mul(rhs)
197 }
198}
199
200impl<const LIMBS: usize, const RHS_LIMBS: usize> Mul<Uint<RHS_LIMBS>> for &Uint<LIMBS> {
201 type Output = Uint<LIMBS>;
202
203 fn mul(self, rhs: Uint<RHS_LIMBS>) -> Self::Output {
204 self.mul(&rhs)
205 }
206}
207
208impl<const LIMBS: usize, const RHS_LIMBS: usize> Mul<&Uint<RHS_LIMBS>> for &Uint<LIMBS> {
209 type Output = Uint<LIMBS>;
210
211 fn mul(self, rhs: &Uint<RHS_LIMBS>) -> Self::Output {
212 self.checked_mul(rhs)
213 .expect("attempted to multiply with overflow")
214 }
215}
216
217impl<const LIMBS: usize, const RHS_LIMBS: usize> MulAssign<Uint<RHS_LIMBS>> for Uint<LIMBS> {
218 fn mul_assign(&mut self, rhs: Uint<RHS_LIMBS>) {
219 *self = self.mul(&rhs);
220 }
221}
222
223impl<const LIMBS: usize, const RHS_LIMBS: usize> MulAssign<&Uint<RHS_LIMBS>> for Uint<LIMBS> {
224 fn mul_assign(&mut self, rhs: &Uint<RHS_LIMBS>) {
225 *self = self.mul(rhs);
226 }
227}
228
229impl<const LIMBS: usize> MulAssign<Wrapping<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
230 fn mul_assign(&mut self, other: Wrapping<Uint<LIMBS>>) {
231 *self = *self * other;
232 }
233}
234
235impl<const LIMBS: usize> MulAssign<&Wrapping<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
236 fn mul_assign(&mut self, other: &Wrapping<Uint<LIMBS>>) {
237 *self = *self * other;
238 }
239}
240
241impl<const LIMBS: usize> MulAssign<Checked<Uint<LIMBS>>> for Checked<Uint<LIMBS>> {
242 fn mul_assign(&mut self, other: Checked<Uint<LIMBS>>) {
243 *self = *self * other;
244 }
245}
246
247impl<const LIMBS: usize> MulAssign<&Checked<Uint<LIMBS>>> for Checked<Uint<LIMBS>> {
248 fn mul_assign(&mut self, other: &Checked<Uint<LIMBS>>) {
249 *self = *self * other;
250 }
251}
252
253impl<const LIMBS: usize, const RHS_LIMBS: usize, const WIDE_LIMBS: usize>
254 ConcatenatingMul<Uint<RHS_LIMBS>> for Uint<LIMBS>
255where
256 Self: Concat<RHS_LIMBS, Output = Uint<WIDE_LIMBS>>,
257{
258 type Output = Uint<WIDE_LIMBS>;
259
260 #[inline]
261 fn concatenating_mul(&self, rhs: Uint<RHS_LIMBS>) -> Self::Output {
262 self.concatenating_mul(&rhs)
263 }
264}
265
266impl<const LIMBS: usize, const RHS_LIMBS: usize, const WIDE_LIMBS: usize>
267 ConcatenatingMul<&Uint<RHS_LIMBS>> for Uint<LIMBS>
268where
269 Self: Concat<RHS_LIMBS, Output = Uint<WIDE_LIMBS>>,
270{
271 type Output = Uint<WIDE_LIMBS>;
272
273 #[inline]
274 fn concatenating_mul(&self, rhs: &Uint<RHS_LIMBS>) -> Self::Output {
275 self.concatenating_mul(rhs)
276 }
277}
278
279impl<const LIMBS: usize, const WIDE_LIMBS: usize> ConcatenatingSquare for Uint<LIMBS>
280where
281 Self: Concat<LIMBS, Output = Uint<WIDE_LIMBS>>,
282{
283 type Output = Uint<WIDE_LIMBS>;
284
285 #[inline]
286 fn concatenating_square(&self) -> Self::Output {
287 self.concatenating_square()
288 }
289}
290
291impl<const LIMBS: usize> WrappingMul for Uint<LIMBS> {
292 fn wrapping_mul(&self, v: &Self) -> Self {
293 self.wrapping_mul(v)
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use crate::{ConcatenatingMul, ConcatenatingSquare, Limb, U64, U128, U192, U256, Uint};
300
301 #[test]
302 fn widening_mul_zero_and_one() {
303 assert_eq!(U64::ZERO.widening_mul(&U64::ZERO), (U64::ZERO, U64::ZERO));
304 assert_eq!(U64::ZERO.widening_mul(&U64::ONE), (U64::ZERO, U64::ZERO));
305 assert_eq!(U64::ONE.widening_mul(&U64::ZERO), (U64::ZERO, U64::ZERO));
306 assert_eq!(U64::ONE.widening_mul(&U64::ONE), (U64::ONE, U64::ZERO));
307 }
308
309 #[test]
310 fn widening_mul_lo_only() {
311 let primes: &[u32] = &[3, 5, 17, 257, 65537];
312
313 for &a_int in primes {
314 for &b_int in primes {
315 let (lo, hi) = U64::from_u32(a_int).widening_mul(&U64::from_u32(b_int));
316 let expected = U64::from_u64(u64::from(a_int) * u64::from(b_int));
317 assert_eq!(lo, expected);
318 assert!(bool::from(hi.is_zero()));
319 assert_eq!(lo, U64::from_u32(a_int).wrapping_mul(&U64::from_u32(b_int)));
320 }
321 }
322 }
323
324 #[test]
325 fn mul_concat_even() {
326 assert_eq!(U64::ZERO.concatenating_mul(&U64::MAX), U128::ZERO);
327 assert_eq!(U64::MAX.concatenating_mul(&U64::ZERO), U128::ZERO);
328 assert_eq!(
329 U64::MAX.concatenating_mul(&U64::MAX),
330 U128::from_u128(0xfffffffffffffffe_0000000000000001)
331 );
332 assert_eq!(
333 U64::ONE.concatenating_mul(&U64::MAX),
334 U128::from_u128(0x0000000000000000_ffffffffffffffff)
335 );
336 }
337
338 #[test]
339 fn mul_concat_mixed() {
340 let a = U64::from_u64(0x0011223344556677);
341 let b = U128::from_u128(0x8899aabbccddeeff_8899aabbccddeeff);
342 let expected = U192::from(&b).saturating_mul(&a);
343 assert_eq!(a.concatenating_mul(&b), expected);
344 assert_eq!(ConcatenatingMul::concatenating_mul(&a, &b), expected);
345 assert_eq!(b.concatenating_mul(&a), expected);
346 assert_eq!(ConcatenatingMul::concatenating_mul(&b, &a), expected);
347 }
348
349 #[test]
350 fn wrapping_mul_even() {
351 assert_eq!(U64::ZERO.wrapping_mul(&U64::MAX), U64::ZERO);
352 assert_eq!(U64::MAX.wrapping_mul(&U64::ZERO), U64::ZERO);
353 assert_eq!(U64::MAX.wrapping_mul(&U64::MAX), U64::ONE);
354 assert_eq!(U64::ONE.wrapping_mul(&U64::MAX), U64::MAX);
355 }
356
357 #[test]
358 fn wrapping_mul_mixed() {
359 let a = U64::from_u64(0x0011223344556677);
360 let b = U128::from_u128(0x8899aabbccddeeff_8899aabbccddeeff);
361 let expected = U192::from(&b).saturating_mul(&a);
362 assert_eq!(b.wrapping_mul(&a), expected.resize());
363 assert_eq!(a.wrapping_mul(&b), expected.resize());
364 }
365
366 #[test]
367 fn checked_mul_ok() {
368 let n = U64::from_u32(0xffff_ffff);
369 assert_eq!(
370 n.checked_mul(&n).unwrap(),
371 U64::from_u64(0xffff_fffe_0000_0001)
372 );
373 assert_eq!(U64::ZERO.checked_mul(&U64::ZERO).unwrap(), U64::ZERO);
374 }
375
376 #[test]
377 fn checked_mul_overflow() {
378 let n = U64::MAX;
379 assert!(bool::from(n.checked_mul(&n).is_none()));
380 }
381
382 #[test]
383 fn saturating_mul_no_overflow() {
384 let n = U64::from_u8(8);
385 assert_eq!(n.saturating_mul(&n), U64::from_u8(64));
386 }
387
388 #[test]
389 fn saturating_mul_overflow() {
390 let a = U64::from(0xffff_ffff_ffff_ffffu64);
391 let b = U64::from(2u8);
392 assert_eq!(a.saturating_mul(&b), U64::MAX);
393 }
394
395 #[test]
396 fn concatenating_square() {
397 let n = U64::from_u64(0xffff_ffff_ffff_ffff);
398 let (lo, hi) = n.concatenating_square().split();
399 assert_eq!(lo, U64::from_u64(1));
400 assert_eq!(hi, U64::from_u64(0xffff_ffff_ffff_fffe));
401 let check = ConcatenatingSquare::concatenating_square(&n).split();
402 assert_eq!(check, (lo, hi));
403 }
404
405 #[test]
406 fn concatenating_square_larger() {
407 let n = U256::MAX;
408 let (lo, hi) = n.concatenating_square().split();
409 assert_eq!(lo, U256::ONE);
410 assert_eq!(hi, U256::MAX.wrapping_sub(&U256::ONE));
411 }
412
413 #[test]
414 fn checked_square() {
415 let n = U256::from_u64(u64::MAX).wrapping_add(&U256::ONE);
416 let n2 = n.checked_square();
417 assert!(n2.is_some().to_bool());
418 let n4 = n2.unwrap().checked_square();
419 assert!(n4.is_none().to_bool());
420 let z = U256::ZERO.checked_square();
421 assert!(z.is_some().to_bool());
422 let m = U256::MAX.checked_square();
423 assert!(m.is_none().to_bool());
424 }
425
426 #[test]
427 fn wrapping_square() {
428 let n = U256::from_u64(u64::MAX).wrapping_add(&U256::ONE);
429 let n2 = n.wrapping_square();
430 assert_eq!(n2, U256::from_u128(u128::MAX).wrapping_add(&U256::ONE));
431 let n4 = n2.wrapping_square();
432 assert_eq!(n4, U256::ZERO);
433 }
434
435 #[test]
436 fn saturating_square() {
437 let n = U256::from_u64(u64::MAX).wrapping_add(&U256::ONE);
438 let n2 = n.saturating_square();
439 assert_eq!(n2, U256::from_u128(u128::MAX).wrapping_add(&U256::ONE));
440 let n4 = n2.saturating_square();
441 assert_eq!(n4, U256::MAX);
442 }
443
444 #[cfg(feature = "rand_core")]
445 #[test]
446 fn mul_cmp() {
447 use crate::{Random, U4096};
448 use rand_core::SeedableRng;
449 let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
450
451 let rounds = if cfg!(miri) { 10 } else { 50 };
452 for _ in 0..rounds {
453 let a = U4096::random_from_rng(&mut rng);
454 assert_eq!(a.concatenating_mul(&a), a.concatenating_square(), "a = {a}");
455 assert_eq!(a.widening_mul(&a), a.widening_square(), "a = {a}");
456 assert_eq!(a.wrapping_mul(&a), a.wrapping_square(), "a = {a}");
457 assert_eq!(a.saturating_mul(&a), a.saturating_square(), "a = {a}");
458 }
459 }
460
461 #[test]
462 fn checked_mul_sizes() {
463 const SIZE_A: usize = 4;
464 const SIZE_B: usize = 8;
465
466 for n in 0..Uint::<SIZE_A>::BITS {
467 let mut a = Uint::<SIZE_A>::ZERO;
468 a = a.set_bit_vartime(n, true);
469
470 for m in (0..Uint::<SIZE_B>::BITS).step_by(16) {
471 let mut b = Uint::<SIZE_B>::ZERO;
472 b = b.set_bit_vartime(m, true);
473 let res = a.widening_mul(&b);
474 let res_overflow = res.1.is_nonzero();
475 let checked = a.checked_mul(&b);
476 assert_eq!(checked.is_some().to_bool(), res_overflow.not().to_bool());
477 assert_eq!(
478 checked.as_inner_unchecked(),
479 &res.0,
480 "a = 2**{n}, b = 2**{m}"
481 );
482 }
483 }
484 }
485
486 #[test]
487 fn checked_square_sizes() {
488 const SIZE: usize = 4;
489
490 for n in 0..Uint::<SIZE>::BITS {
491 let mut a = Uint::<SIZE>::ZERO;
492 a = a.set_bit_vartime(n, true);
493
494 let res = a.widening_square();
495 let res_overflow = res.1.is_nonzero();
496 let checked = a.checked_square();
497 assert_eq!(checked.is_some().to_bool(), res_overflow.not().to_bool());
498 assert_eq!(checked.as_inner_unchecked(), &res.0, "a = 2**{n}");
499 }
500 }
501
502 #[test]
503 fn overflowing_mul_limb() {
504 let (max_lo, max_hi) = U128::MAX.widening_mul(&U128::from(Limb::MAX));
505
506 let result = U128::ZERO.overflowing_mul_limb(Limb::ZERO);
507 assert_eq!(result, (U128::ZERO, Limb::ZERO));
508 let result = U128::ZERO.overflowing_mul_limb(Limb::ONE);
509 assert_eq!(result, (U128::ZERO, Limb::ZERO));
510 let result = U128::MAX.overflowing_mul_limb(Limb::ZERO);
511 assert_eq!(result, (U128::ZERO, Limb::ZERO));
512 let result = U128::MAX.overflowing_mul_limb(Limb::ONE);
513 assert_eq!(result, (U128::MAX, Limb::ZERO));
514 let result = U128::MAX.overflowing_mul_limb(Limb::MAX);
515 assert_eq!(result, (max_lo, max_hi.limbs[0]));
516
517 assert_eq!(U128::ZERO.wrapping_mul_limb(Limb::ZERO), U128::ZERO);
518 assert_eq!(U128::ZERO.wrapping_mul_limb(Limb::ONE), U128::ZERO);
519 assert_eq!(U128::MAX.wrapping_mul_limb(Limb::ZERO), U128::ZERO);
520 assert_eq!(U128::MAX.wrapping_mul_limb(Limb::ONE), U128::MAX);
521 assert_eq!(U128::MAX.wrapping_mul_limb(Limb::MAX), max_lo);
522 }
523}