|
1 | 1 | //! [`Uint`] addition operations. |
2 | 2 |
|
3 | | -use crate::{Checked, CheckedMul, Concat, Limb, Uint, Wrapping, Zero}; |
| 3 | +use crate::{Checked, CheckedMul, Concat, Limb, Uint, WideWord, Word, Wrapping, Zero}; |
4 | 4 | use core::ops::{Mul, MulAssign}; |
5 | 5 | use subtle::CtOption; |
6 | 6 |
|
@@ -86,7 +86,76 @@ impl<const LIMBS: usize> Uint<LIMBS> { |
86 | 86 |
|
87 | 87 | /// Square self, returning a "wide" result in two parts as (lo, hi). |
88 | 88 | pub const fn square_wide(&self) -> (Self, Self) { |
89 | | - self.mul_wide(self) |
| 89 | + // Translated from https://github.com/ucbrise/jedi-pairing/blob/c4bf151/include/core/bigint.hpp#L410 |
| 90 | + // |
| 91 | + // Permission to relicense the resulting translation as Apache 2.0 + MIT was given |
| 92 | + // by the original author Sam Kumar: https://github.com/RustCrypto/crypto-bigint/pull/133#discussion_r1056870411 |
| 93 | + let mut lo = Self::ZERO; |
| 94 | + let mut hi = Self::ZERO; |
| 95 | + |
| 96 | + // Schoolbook multiplication, but only considering half of the multiplication grid |
| 97 | + let mut i = 1; |
| 98 | + while i < LIMBS { |
| 99 | + let mut j = 0; |
| 100 | + let mut carry = Limb::ZERO; |
| 101 | + |
| 102 | + while j < i { |
| 103 | + let k = i + j; |
| 104 | + |
| 105 | + if k >= LIMBS { |
| 106 | + let (n, c) = hi.limbs[k - LIMBS].mac(self.limbs[i], self.limbs[j], carry); |
| 107 | + hi.limbs[k - LIMBS] = n; |
| 108 | + carry = c; |
| 109 | + } else { |
| 110 | + let (n, c) = lo.limbs[k].mac(self.limbs[i], self.limbs[j], carry); |
| 111 | + lo.limbs[k] = n; |
| 112 | + carry = c; |
| 113 | + } |
| 114 | + |
| 115 | + j += 1; |
| 116 | + } |
| 117 | + |
| 118 | + if (2 * i) < LIMBS { |
| 119 | + lo.limbs[2 * i] = carry; |
| 120 | + } else { |
| 121 | + hi.limbs[2 * i - LIMBS] = carry; |
| 122 | + } |
| 123 | + |
| 124 | + i += 1; |
| 125 | + } |
| 126 | + |
| 127 | + // Double the current result, this accounts for the other half of the multiplication grid. |
| 128 | + // TODO: The top word is empty so we can also use a special purpose shl. |
| 129 | + (lo, hi) = Self::shl_vartime_wide((lo, hi), 1); |
| 130 | + |
| 131 | + // Handle the diagonal of the multiplication grid, which finishes the multiplication grid. |
| 132 | + let mut carry = Limb::ZERO; |
| 133 | + let mut i = 0; |
| 134 | + while i < LIMBS { |
| 135 | + if (i * 2) < LIMBS { |
| 136 | + let (n, c) = lo.limbs[i * 2].mac(self.limbs[i], self.limbs[i], carry); |
| 137 | + lo.limbs[i * 2] = n; |
| 138 | + carry = c; |
| 139 | + } else { |
| 140 | + let (n, c) = hi.limbs[i * 2 - LIMBS].mac(self.limbs[i], self.limbs[i], carry); |
| 141 | + hi.limbs[i * 2 - LIMBS] = n; |
| 142 | + carry = c; |
| 143 | + } |
| 144 | + |
| 145 | + if (i * 2 + 1) < LIMBS { |
| 146 | + let n = lo.limbs[i * 2 + 1].0 as WideWord + carry.0 as WideWord; |
| 147 | + lo.limbs[i * 2 + 1] = Limb(n as Word); |
| 148 | + carry = Limb((n >> Word::BITS) as Word); |
| 149 | + } else { |
| 150 | + let n = hi.limbs[i * 2 + 1 - LIMBS].0 as WideWord + carry.0 as WideWord; |
| 151 | + hi.limbs[i * 2 + 1 - LIMBS] = Limb(n as Word); |
| 152 | + carry = Limb((n >> Word::BITS) as Word); |
| 153 | + } |
| 154 | + |
| 155 | + i += 1; |
| 156 | + } |
| 157 | + |
| 158 | + (lo, hi) |
90 | 159 | } |
91 | 160 | } |
92 | 161 |
|
@@ -189,7 +258,7 @@ impl<const LIMBS: usize> MulAssign<&Checked<Uint<LIMBS>>> for Checked<Uint<LIMBS |
189 | 258 |
|
190 | 259 | #[cfg(test)] |
191 | 260 | mod tests { |
192 | | - use crate::{CheckedMul, Zero, U64}; |
| 261 | + use crate::{CheckedMul, Zero, U256, U64}; |
193 | 262 |
|
194 | 263 | #[test] |
195 | 264 | fn mul_wide_zero_and_one() { |
@@ -248,4 +317,12 @@ mod tests { |
248 | 317 | assert_eq!(lo, U64::from_u64(1)); |
249 | 318 | assert_eq!(hi, U64::from_u64(0xffff_ffff_ffff_fffe)); |
250 | 319 | } |
| 320 | + |
| 321 | + #[test] |
| 322 | + fn square_larger() { |
| 323 | + let n = U256::MAX; |
| 324 | + let (hi, lo) = n.square().split(); |
| 325 | + assert_eq!(lo, U256::ONE); |
| 326 | + assert_eq!(hi, U256::MAX.wrapping_sub(&U256::ONE)); |
| 327 | + } |
251 | 328 | } |
0 commit comments