crypto_bigint/modular/bingcd/
extension.rs1use crate::{Choice, CtOption, Int, Limb, Uint};
2
3pub(crate) struct ExtendedUint<const LIMBS: usize, const EXTENSION_LIMBS: usize>(
4 Uint<LIMBS>,
5 Uint<EXTENSION_LIMBS>,
6);
7
8impl<const LIMBS: usize, const EXTRA: usize> ExtendedUint<LIMBS, EXTRA> {
9 #[inline]
13 pub const fn from_product(lhs: Uint<LIMBS>, rhs: Uint<EXTRA>) -> Self {
14 let (lo, hi) = lhs.widening_mul(&rhs);
15 ExtendedUint(lo, hi)
16 }
17
18 pub const fn wrapping_mul<const RHS_LIMBS: usize>(&self, rhs: &Uint<RHS_LIMBS>) -> Self {
20 let (lo, hi) = self.0.widening_mul(rhs);
21 let hi = self.1.wrapping_mul(rhs).wrapping_add(&hi.resize::<EXTRA>());
22 Self(lo, hi)
23 }
24
25 #[inline]
27 pub const fn as_extended_int(&self) -> ExtendedInt<LIMBS, EXTRA> {
28 ExtendedInt(self.0, self.1)
29 }
30
31 #[inline]
33 pub const fn is_zero(&self) -> Choice {
34 self.0.is_nonzero().not().and(self.1.is_nonzero().not())
35 }
36
37 #[inline]
39 pub const fn checked_drop_extension(&self) -> CtOption<Uint<LIMBS>> {
40 CtOption::new(self.0, self.1.is_nonzero().not())
41 }
42
43 #[inline]
47 pub const fn wrapping_neg(&self) -> Self {
48 let (lhs, carry) = self.0.carrying_neg();
49 let mut rhs = self.1.not();
50 rhs = Uint::select(&rhs, &rhs.wrapping_add(&Uint::ONE), carry);
51 Self(lhs, rhs)
52 }
53
54 #[inline]
56 pub const fn wrapping_neg_if(&self, negate: Choice) -> Self {
57 let neg = self.wrapping_neg();
58 Self(
59 Uint::select(&self.0, &neg.0, negate),
60 Uint::select(&self.1, &neg.1, negate),
61 )
62 }
63
64 #[inline]
69 #[allow(clippy::cast_possible_truncation)]
70 pub const fn bounded_shr<const UPPER_BOUND: u32>(&self, shift: u32) -> Self {
71 assert!(shift < UPPER_BOUND && UPPER_BOUND <= Uint::<EXTRA>::BITS);
72
73 let hi = self.1.bounded_shr(shift, UPPER_BOUND);
74 let carry = self.1.unbounded_shl(Uint::<EXTRA>::BITS - shift);
75 let mut lo = self.0.bounded_shr(shift, UPPER_BOUND);
76
77 let limb_diff = LIMBS.saturating_sub(EXTRA) as u32;
79 let carry = carry
82 .resize::<LIMBS>()
83 .unbounded_shl_by_limbs_vartime(limb_diff);
84 lo = lo.bitxor(&carry);
85
86 Self(lo, hi)
87 }
88
89 #[inline]
94 #[allow(clippy::cast_possible_truncation)]
95 pub const fn shr_vartime(&self, shift: u32) -> Self {
96 debug_assert!(shift <= Uint::<EXTRA>::BITS);
97
98 let hi = self.1.shr_vartime(shift);
99 let carry = self.1.unbounded_shl_vartime(Uint::<EXTRA>::BITS - shift);
100 let mut lo = self.0.shr_vartime(shift);
101
102 let limb_diff = LIMBS.saturating_sub(EXTRA) as u32;
104 let carry = carry
107 .resize::<LIMBS>()
108 .unbounded_shl_by_limbs_vartime(limb_diff);
109 lo = lo.bitxor(&carry);
110
111 Self(lo, hi)
112 }
113}
114
115#[derive(Debug, PartialEq, Clone, Copy)]
116pub(crate) struct ExtendedInt<const LIMBS: usize, const EXTENSION_LIMBS: usize>(
117 Uint<LIMBS>,
118 Uint<EXTENSION_LIMBS>,
119);
120
121impl<const LIMBS: usize, const EXTRA: usize> ExtendedInt<LIMBS, EXTRA> {
122 pub(super) const ZERO: Self = Self(Uint::ZERO, Uint::ZERO);
123 pub(super) const ONE: Self = Self(Uint::ONE, Uint::ZERO);
124
125 #[inline]
129 pub const fn from_product(lhs: Uint<LIMBS>, rhs: Uint<EXTRA>) -> Self {
130 ExtendedUint::from_product(lhs, rhs).as_extended_int()
131 }
132
133 pub(crate) const fn wrapping_mul<const RHS_LIMBS: usize>(
135 &self,
136 rhs: (&Uint<RHS_LIMBS>, &Choice),
137 ) -> Self {
138 let (abs_self, self_is_negative) = self.abs_sign();
139 let (abs_rhs, rhs_is_negative) = rhs;
140 let mut abs_val = abs_self.wrapping_mul(abs_rhs);
141
142 abs_val.1 = abs_val.1.bitand(Int::<EXTRA>::SIGN_MASK.not().as_uint());
144
145 let val_is_negative = self_is_negative.xor(*rhs_is_negative);
146 abs_val.wrapping_neg_if(val_is_negative).as_extended_int()
147 }
148
149 #[inline]
151 pub const fn as_extended_uint(&self) -> ExtendedUint<LIMBS, EXTRA> {
152 ExtendedUint(self.0, self.1)
153 }
154
155 #[inline]
157 pub const fn wrapping_neg_if(&self, negate: Choice) -> Self {
158 self.as_extended_uint()
159 .wrapping_neg_if(negate)
160 .as_extended_int()
161 }
162
163 #[inline]
164 pub(crate) const fn wrapping_add(&self, rhs: &Self) -> Self {
165 let (lo, carry) = self.0.carrying_add(&rhs.0, Limb::ZERO);
166 let (hi, _) = self.1.carrying_add(&rhs.1, carry);
167 Self(lo, hi)
168 }
169
170 #[inline]
172 pub const fn wrapping_sub(&self, rhs: &Self) -> Self {
173 let (lo, borrow) = self.0.borrowing_sub(&rhs.0, Limb::ZERO);
174 let (hi, _) = self.1.borrowing_sub(&rhs.1, borrow);
175 Self(lo, hi)
176 }
177
178 #[inline]
180 pub const fn dropped_abs_sign(&self) -> (Uint<LIMBS>, Choice) {
181 let (abs, sgn) = self.abs_sign();
182 (abs.0, sgn)
183 }
184
185 #[inline]
187 pub const fn abs_sign(&self) -> (ExtendedUint<LIMBS, EXTRA>, Choice) {
188 let is_negative = self.1.as_int().is_negative();
189 (
190 self.wrapping_neg_if(is_negative).as_extended_uint(),
191 is_negative,
192 )
193 }
194
195 #[inline]
200 pub const fn bounded_div_2k<const UPPER_BOUND: u32>(&self, k: u32) -> Self {
201 let (abs, sgn) = self.abs_sign();
202 abs.bounded_shr::<UPPER_BOUND>(k)
203 .wrapping_neg_if(sgn)
204 .as_extended_int()
205 }
206
207 #[inline]
209 pub const fn div_2k_vartime(&self, k: u32) -> Self {
210 let (abs, sgn) = self.abs_sign();
211 abs.shr_vartime(k).wrapping_neg_if(sgn).as_extended_int()
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::ExtendedUint;
218 use crate::{U64, Uint};
219
220 impl<const LIMBS: usize, const EXTRA: usize> ExtendedUint<LIMBS, EXTRA> {
221 #[inline]
223 const fn as_elements(&self) -> (Uint<LIMBS>, Uint<EXTRA>) {
224 (self.0, self.1)
225 }
226 }
227
228 const A: ExtendedUint<{ U64::LIMBS }, { U64::LIMBS }> = ExtendedUint::from_product(
229 U64::from_u64(68146184546341u64),
230 U64::from_u64(873817114763u64),
231 );
232 const B: ExtendedUint<{ U64::LIMBS }, { U64::LIMBS }> = ExtendedUint::from_product(
233 U64::from_u64(7772181434148543u64),
234 U64::from_u64(6665138352u64),
235 );
236
237 #[test]
238 fn bounded_shr() {
239 let a = ExtendedUint::<4, 2>(Uint::ZERO, Uint::MAX);
240 let actual = a.bounded_shr::<32>(20);
241 assert_eq!(
242 actual.as_elements(),
243 (Uint::MAX.shl(Uint::<4>::BITS - 20), Uint::MAX.shr(20)),
244 );
245 }
246
247 #[test]
248 fn test_from_product() {
249 assert_eq!(
250 A.as_elements(),
251 (U64::from(13454091406951429143u64), U64::from(3228065u64))
252 );
253 assert_eq!(
254 B.as_elements(),
255 (U64::from(1338820589698724688u64), U64::from(2808228u64))
256 );
257 }
258
259 #[test]
260 fn test_wrapping_sub() {
261 assert_eq!(
262 A.as_extended_int()
263 .wrapping_sub(&B.as_extended_int())
264 .as_extended_uint()
265 .as_elements(),
266 (U64::from(12115270817252704455u64), U64::from(419837u64))
267 );
268 }
269}