1use super::{HeaplessBigInt, zero};
24use crate::MachineWord;
25use const_num_traits::{Bounded, Personality, PersonalityTag};
26use core::marker::PhantomData;
27use core::ops::{Shl, ShlAssign, Shr, ShrAssign};
28
29fn shl_wp<T: MachineWord, const CAP: usize, P: Personality>(
35 value: HeaplessBigInt<T, CAP, P>,
36 bits: usize,
37) -> HeaplessBigInt<T, CAP, P> {
38 let word_bits = core::mem::size_of::<T>() * 8;
39 let word_shift = bits / word_bits;
40 let bit_shift = bits % word_bits;
41 let out_len = value.len as usize;
42 let mut limbs = [zero::<T>(); CAP];
43 let mut i = 0;
44 while i < out_len {
45 let dst_lo = i + word_shift;
46 if dst_lo < out_len {
47 limbs[dst_lo] |= value.limbs[i] << bit_shift;
48 if bit_shift > 0 {
49 let dst_hi = dst_lo + 1;
50 if dst_hi < out_len {
51 limbs[dst_hi] |= value.limbs[i] >> (word_bits - bit_shift);
52 }
53 }
54 }
55 i += 1;
56 }
57 HeaplessBigInt {
58 limbs,
59 len: value.len,
60 _p: PhantomData,
61 }
62}
63
64fn shr_wp<T: MachineWord, const CAP: usize, P: Personality>(
69 value: HeaplessBigInt<T, CAP, P>,
70 bits: usize,
71) -> HeaplessBigInt<T, CAP, P> {
72 let word_bits = core::mem::size_of::<T>() * 8;
73 let word_shift = bits / word_bits;
74 let bit_shift = bits % word_bits;
75 let n = value.len as usize;
76 let mut limbs = [zero::<T>(); CAP];
77 let mut i = 0;
78 while i < n {
79 let src_lo = i + word_shift;
80 let lo = if src_lo < n {
81 value.limbs[src_lo] >> bit_shift
82 } else {
83 zero::<T>()
84 };
85 let hi = if bit_shift > 0 && src_lo + 1 < n {
86 value.limbs[src_lo + 1] << (word_bits - bit_shift)
87 } else {
88 zero::<T>()
89 };
90 limbs[i] = lo | hi;
91 i += 1;
92 }
93 HeaplessBigInt {
94 limbs,
95 len: value.len,
96 _p: PhantomData,
97 }
98}
99
100#[inline]
105fn ct_mask<T: MachineWord>(choice_bit: u8) -> T {
106 let bit = core::hint::black_box(choice_bit & 1);
107 let bit_t = <T as core::convert::From<u8>>::from(bit);
108 <T as core::ops::Mul>::mul(bit_t, <T as Bounded>::max_value())
109}
110
111pub(crate) fn const_shl_ct<T: MachineWord, const CAP: usize, P: Personality>(
117 value: HeaplessBigInt<T, CAP, P>,
118 bits: usize,
119) -> HeaplessBigInt<T, CAP, P> {
120 let n = value.len as usize;
121 let layers = core::mem::size_of::<usize>() * 8;
126 let mut target = value;
127 let mut k = 0;
128 while k < layers {
129 let shifted = shl_wp(target, 1usize << k);
130 let mask = ct_mask::<T>(((bits >> k) & 1) as u8);
131 let mut i = 0;
132 while i < n {
133 let diff = target.limbs[i] ^ shifted.limbs[i];
134 target.limbs[i] ^= mask & diff;
135 i += 1;
136 }
137 k += 1;
138 }
139 target
140}
141
142pub(crate) fn const_shr_ct<T: MachineWord, const CAP: usize, P: Personality>(
145 value: HeaplessBigInt<T, CAP, P>,
146 bits: usize,
147) -> HeaplessBigInt<T, CAP, P> {
148 let n = value.len as usize;
149 let layers = core::mem::size_of::<usize>() * 8;
150 let mut target = value;
151 let mut k = 0;
152 while k < layers {
153 let shifted = shr_wp(target, 1usize << k);
154 let mask = ct_mask::<T>(((bits >> k) & 1) as u8);
155 let mut i = 0;
156 while i < n {
157 let diff = target.limbs[i] ^ shifted.limbs[i];
158 target.limbs[i] ^= mask & diff;
159 i += 1;
160 }
161 k += 1;
162 }
163 target
164}
165
166pub(crate) fn ct_shl<T: MachineWord, const CAP: usize, P: Personality>(
169 value: HeaplessBigInt<T, CAP, P>,
170 amount: u32,
171) -> HeaplessBigInt<T, CAP, P> {
172 const_shl_ct(value, amount as usize)
173}
174
175impl<T: MachineWord, const CAP: usize, P: Personality> Shl<u32> for HeaplessBigInt<T, CAP, P> {
186 type Output = Self;
187 fn shl(self, bits: u32) -> Self::Output {
188 match P::TAG {
189 PersonalityTag::Ct => const_shl_ct(self, bits as usize),
194 PersonalityTag::Nct => {
195 let value_bits = self.len as u32 * (core::mem::size_of::<T>() as u32 * 8);
196 if bits >= value_bits {
197 Self::new_zero_with_len(self.len())
198 } else {
199 self << (bits as usize)
200 }
201 }
202 }
203 }
204}
205
206impl<T: MachineWord, const CAP: usize, P: Personality> Shr<u32> for HeaplessBigInt<T, CAP, P> {
207 type Output = Self;
208 fn shr(self, bits: u32) -> Self::Output {
209 match P::TAG {
210 PersonalityTag::Ct => const_shr_ct(self, bits as usize),
211 PersonalityTag::Nct => {
212 let value_bits = self.len as u32 * (core::mem::size_of::<T>() as u32 * 8);
213 if bits >= value_bits {
214 Self::new_zero_with_len(0)
215 } else {
216 self >> (bits as usize)
217 }
218 }
219 }
220 }
221}
222
223impl<T: MachineWord, const CAP: usize, P: Personality> Shl<usize> for HeaplessBigInt<T, CAP, P> {
224 type Output = Self;
225
226 fn shl(self, bits: usize) -> Self::Output {
227 match P::TAG {
231 PersonalityTag::Nct => shl_wp(self, bits),
232 PersonalityTag::Ct => const_shl_ct(self, bits),
233 }
234 }
235}
236
237impl<T: MachineWord, const CAP: usize, P: Personality> ShlAssign<usize>
238 for HeaplessBigInt<T, CAP, P>
239{
240 fn shl_assign(&mut self, bits: usize) {
241 *self = *self << bits;
242 }
243}
244
245impl<T: MachineWord, const CAP: usize, P: Personality> ShrAssign<usize>
246 for HeaplessBigInt<T, CAP, P>
247{
248 fn shr_assign(&mut self, bits: usize) {
249 *self = *self >> bits;
250 }
251}
252
253impl<T: MachineWord, const CAP: usize, P: Personality> Shr<usize> for HeaplessBigInt<T, CAP, P> {
254 type Output = Self;
255
256 fn shr(self, bits: usize) -> Self::Output {
257 match P::TAG {
258 PersonalityTag::Ct => const_shr_ct(self, bits),
261 PersonalityTag::Nct => {
264 let word_bits = core::mem::size_of::<T>() * 8;
265 let word_shift = bits / word_bits;
266 let bit_shift = bits % word_bits;
267
268 let mut limbs = [zero::<T>(); CAP];
269 if word_shift >= self.len as usize {
270 return Self {
271 limbs,
272 len: 0,
273 _p: PhantomData,
274 };
275 }
276
277 let out_len = self.len as usize - word_shift;
278
279 let mut i = 0;
280 while i < out_len {
281 let src_lo = i + word_shift;
282 let lo = self.limbs[src_lo] >> bit_shift;
283 let hi = if bit_shift > 0 && src_lo + 1 < self.len as usize {
284 self.limbs[src_lo + 1] << (word_bits - bit_shift)
285 } else {
286 zero::<T>()
287 };
288 limbs[i] = lo | hi;
289 i += 1;
290 }
291
292 Self {
293 limbs,
294 len: out_len as u16,
295 _p: PhantomData,
296 }
297 }
298 }
299 }
300}
301
302macro_rules! shift_operand_forms {
310 ($imp:ident, $method:ident, $op:tt, $scalar:ty) => {
311 impl<T: MachineWord, const CAP: usize, P: Personality> $imp<&$scalar>
312 for HeaplessBigInt<T, CAP, P>
313 {
314 type Output = HeaplessBigInt<T, CAP, P>;
315 fn $method(self, bits: &$scalar) -> Self::Output {
316 self $op *bits
317 }
318 }
319
320 impl<T: MachineWord, const CAP: usize, P: Personality> $imp<$scalar>
321 for &HeaplessBigInt<T, CAP, P>
322 {
323 type Output = HeaplessBigInt<T, CAP, P>;
324 fn $method(self, bits: $scalar) -> Self::Output {
325 *self $op bits
326 }
327 }
328
329 impl<T: MachineWord, const CAP: usize, P: Personality> $imp<&$scalar>
330 for &HeaplessBigInt<T, CAP, P>
331 {
332 type Output = HeaplessBigInt<T, CAP, P>;
333 fn $method(self, bits: &$scalar) -> Self::Output {
334 *self $op *bits
335 }
336 }
337 };
338}
339
340shift_operand_forms!(Shl, shl, <<, usize);
341shift_operand_forms!(Shl, shl, <<, u32);
342shift_operand_forms!(Shr, shr, >>, usize);
343shift_operand_forms!(Shr, shr, >>, u32);
344
345impl<T: MachineWord, const CAP: usize, P: Personality> ShlAssign<u32>
349 for HeaplessBigInt<T, CAP, P>
350{
351 fn shl_assign(&mut self, bits: u32) {
352 *self = *self << bits;
353 }
354}
355
356impl<T: MachineWord, const CAP: usize, P: Personality> ShrAssign<u32>
357 for HeaplessBigInt<T, CAP, P>
358{
359 fn shr_assign(&mut self, bits: u32) {
360 *self = *self >> bits;
361 }
362}
363
364impl<T: MachineWord, const CAP: usize, P: Personality> ShlAssign<&usize>
365 for HeaplessBigInt<T, CAP, P>
366{
367 fn shl_assign(&mut self, bits: &usize) {
368 *self = *self << *bits;
369 }
370}
371
372impl<T: MachineWord, const CAP: usize, P: Personality> ShrAssign<&usize>
373 for HeaplessBigInt<T, CAP, P>
374{
375 fn shr_assign(&mut self, bits: &usize) {
376 *self = *self >> *bits;
377 }
378}
379
380impl<T: MachineWord, const CAP: usize, P: Personality> ShlAssign<&u32>
381 for HeaplessBigInt<T, CAP, P>
382{
383 fn shl_assign(&mut self, bits: &u32) {
384 *self = *self << *bits;
385 }
386}
387
388impl<T: MachineWord, const CAP: usize, P: Personality> ShrAssign<&u32>
389 for HeaplessBigInt<T, CAP, P>
390{
391 fn shr_assign(&mut self, bits: &u32) {
392 *self = *self >> *bits;
393 }
394}
395
396#[cfg(test)]
397mod ct_shl_tests {
398 use super::{HeaplessBigInt, ct_shl};
399 use const_num_traits::{Ct, Nct};
400
401 type HC = HeaplessBigInt<u8, 4, Ct>; type HN = HeaplessBigInt<u8, 4, Nct>;
403
404 #[test]
405 fn ct_shl_matches_plain_shift_all_amounts() {
406 for &raw in &[1u32, 0x1234_5678, 0xFFFF_FFFF, 0x8000_0000] {
409 let v = HC::from(raw);
410 for amount in 0u32..=40 {
411 assert_eq!(
412 ct_shl(v, amount),
413 v << (amount as usize),
414 "ct_shl({raw:#x}, {amount})"
415 );
416 }
417 }
418 }
419
420 #[test]
428 fn ct_shifts_match_nct_reference() {
429 let cases = [
430 [1u8, 0, 0, 0],
431 [0x78, 0x56, 0x34, 0x12],
432 [0xFF, 0xFF, 0xFF, 0xFF],
433 [0, 0, 0, 0x80],
434 ];
435 for a in cases {
436 for amount in 0usize..=40 {
437 assert_eq!(
438 (HC::from_limbs(a, 4) << amount).all_limbs(),
439 (HN::from_limbs(a, 4) << amount).all_limbs(),
440 "shl {a:?} << {amount}"
441 );
442 assert_eq!(
443 (HC::from_limbs(a, 4) >> amount).all_limbs(),
444 (HN::from_limbs(a, 4) >> amount).all_limbs(),
445 "shr {a:?} >> {amount}"
446 );
447 }
448 }
449 }
450}