1use super::HeaplessBigInt;
16use crate::MachineWord;
17use const_num_traits::{
18 CheckedShl, CheckedShr, FunnelShl, FunnelShr, OverflowingShl, OverflowingShr, Personality,
19 PersonalityTag, PrimBits, ShlExact, ShrExact, UnboundedShl, UnboundedShr, WrappingShl,
20 WrappingShr,
21};
22
23impl<T: MachineWord, const CAP: usize, P: Personality> HeaplessBigInt<T, CAP, P> {
24 #[inline]
27 fn value_bits(&self) -> u32 {
28 self.len as u32 * (core::mem::size_of::<T>() as u32 * 8)
29 }
30}
31
32#[inline]
35fn normalize_shift(bits: u32, value_bits: u32) -> (usize, bool) {
36 if value_bits == 0 {
37 (0, true)
38 } else if bits >= value_bits {
39 ((bits % value_bits) as usize, true)
40 } else {
41 (bits as usize, false)
42 }
43}
44
45impl<T: MachineWord, const CAP: usize, P: Personality> OverflowingShl
46 for HeaplessBigInt<T, CAP, P>
47{
48 type Output = Self;
49 fn overflowing_shl(self, bits: u32) -> (Self, bool) {
50 let (shift, overflow) = normalize_shift(bits, self.value_bits());
51 (self << shift, overflow)
52 }
53}
54
55impl<T: MachineWord, const CAP: usize, P: Personality> OverflowingShr
56 for HeaplessBigInt<T, CAP, P>
57{
58 type Output = Self;
59 fn overflowing_shr(self, bits: u32) -> (Self, bool) {
60 let (shift, overflow) = normalize_shift(bits, self.value_bits());
61 (self >> shift, overflow)
62 }
63}
64
65impl<T: MachineWord, const CAP: usize, P: Personality> WrappingShl for HeaplessBigInt<T, CAP, P> {
66 type Output = Self;
67 fn wrapping_shl(self, bits: u32) -> Self {
68 OverflowingShl::overflowing_shl(self, bits).0
69 }
70}
71
72impl<T: MachineWord, const CAP: usize, P: Personality> WrappingShr for HeaplessBigInt<T, CAP, P> {
73 type Output = Self;
74 fn wrapping_shr(self, bits: u32) -> Self {
75 OverflowingShr::overflowing_shr(self, bits).0
76 }
77}
78
79impl<T: MachineWord, const CAP: usize, P: Personality> CheckedShl for HeaplessBigInt<T, CAP, P> {
80 type Output = Self;
81 fn checked_shl(self, bits: u32) -> Option<Self> {
82 let (res, overflow) = OverflowingShl::overflowing_shl(self, bits);
83 if overflow { None } else { Some(res) }
84 }
85}
86
87impl<T: MachineWord, const CAP: usize, P: Personality> CheckedShr for HeaplessBigInt<T, CAP, P> {
88 type Output = Self;
89 fn checked_shr(self, bits: u32) -> Option<Self> {
90 let (res, overflow) = OverflowingShr::overflowing_shr(self, bits);
91 if overflow { None } else { Some(res) }
92 }
93}
94
95impl<T: MachineWord, const CAP: usize, P: Personality> UnboundedShl for HeaplessBigInt<T, CAP, P> {
99 type Output = Self;
100 fn unbounded_shl(self, rhs: u32) -> Self {
101 match P::TAG {
102 PersonalityTag::Ct => self << (rhs as usize),
105 PersonalityTag::Nct => {
106 if rhs >= self.value_bits() {
107 Self::new_zero_with_len(self.len())
108 } else {
109 self << (rhs as usize)
110 }
111 }
112 }
113 }
114}
115
116impl<T: MachineWord, const CAP: usize, P: Personality> UnboundedShr for HeaplessBigInt<T, CAP, P> {
117 type Output = Self;
118 fn unbounded_shr(self, rhs: u32) -> Self {
119 match P::TAG {
120 PersonalityTag::Ct => self >> (rhs as usize),
121 PersonalityTag::Nct => {
122 if rhs >= self.value_bits() {
123 Self::new_zero_with_len(self.len())
124 } else {
125 self >> (rhs as usize)
126 }
127 }
128 }
129 }
130}
131
132impl<T: MachineWord, const CAP: usize, P: Personality> ShlExact for HeaplessBigInt<T, CAP, P> {
135 type Output = Self;
136 fn shl_exact(self, rhs: u32) -> Option<Self> {
137 if rhs < self.value_bits() && rhs <= PrimBits::leading_zeros(self) {
138 Some(self << (rhs as usize))
139 } else {
140 None
141 }
142 }
143}
144
145impl<T: MachineWord, const CAP: usize, P: Personality> ShrExact for HeaplessBigInt<T, CAP, P> {
146 type Output = Self;
147 fn shr_exact(self, rhs: u32) -> Option<Self> {
148 if rhs < self.value_bits() && rhs <= PrimBits::trailing_zeros(self) {
149 let width = self.len();
154 Some((self >> (rhs as usize)).widened(width))
155 } else {
156 None
157 }
158 }
159}
160
161impl<T: MachineWord, const CAP: usize, P: Personality> FunnelShl for HeaplessBigInt<T, CAP, P> {
168 type Output = Self;
169 fn funnel_shl(self, rhs: Self, n: u32) -> Self {
170 if n == 0 {
171 return self;
172 }
173 assert!(
174 self.len() == rhs.len(),
175 "HeaplessBigInt::funnel_shl: operands must share a width"
176 );
177 let bits = self.value_bits();
178 assert!(n < bits, "HeaplessBigInt::funnel_shl: n out of range");
179 let lo_shift = bits - n;
180 (self << (n as usize)) | (rhs >> (lo_shift as usize))
181 }
182}
183
184impl<T: MachineWord, const CAP: usize, P: Personality> FunnelShr for HeaplessBigInt<T, CAP, P> {
185 type Output = Self;
186 fn funnel_shr(self, rhs: Self, n: u32) -> Self {
187 if n == 0 {
188 return rhs;
189 }
190 assert!(
191 self.len() == rhs.len(),
192 "HeaplessBigInt::funnel_shr: operands must share a width"
193 );
194 let bits = self.value_bits();
195 assert!(n < bits, "HeaplessBigInt::funnel_shr: n out of range");
196 let hi_shift = bits - n;
197 (rhs >> (n as usize)) | (self << (hi_shift as usize))
198 }
199}
200
201impl<T: MachineWord, const CAP: usize, P: Personality> OverflowingShl
206 for &HeaplessBigInt<T, CAP, P>
207{
208 type Output = HeaplessBigInt<T, CAP, P>;
209 fn overflowing_shl(self, bits: u32) -> (HeaplessBigInt<T, CAP, P>, bool) {
210 <HeaplessBigInt<T, CAP, P> as OverflowingShl>::overflowing_shl(*self, bits)
211 }
212}
213
214impl<T: MachineWord, const CAP: usize, P: Personality> OverflowingShr
215 for &HeaplessBigInt<T, CAP, P>
216{
217 type Output = HeaplessBigInt<T, CAP, P>;
218 fn overflowing_shr(self, bits: u32) -> (HeaplessBigInt<T, CAP, P>, bool) {
219 <HeaplessBigInt<T, CAP, P> as OverflowingShr>::overflowing_shr(*self, bits)
220 }
221}
222
223impl<T: MachineWord, const CAP: usize, P: Personality> WrappingShl for &HeaplessBigInt<T, CAP, P> {
224 type Output = HeaplessBigInt<T, CAP, P>;
225 fn wrapping_shl(self, bits: u32) -> HeaplessBigInt<T, CAP, P> {
226 <HeaplessBigInt<T, CAP, P> as WrappingShl>::wrapping_shl(*self, bits)
227 }
228}
229
230impl<T: MachineWord, const CAP: usize, P: Personality> WrappingShr for &HeaplessBigInt<T, CAP, P> {
231 type Output = HeaplessBigInt<T, CAP, P>;
232 fn wrapping_shr(self, bits: u32) -> HeaplessBigInt<T, CAP, P> {
233 <HeaplessBigInt<T, CAP, P> as WrappingShr>::wrapping_shr(*self, bits)
234 }
235}
236
237impl<T: MachineWord, const CAP: usize, P: Personality> CheckedShl for &HeaplessBigInt<T, CAP, P> {
238 type Output = HeaplessBigInt<T, CAP, P>;
239 fn checked_shl(self, bits: u32) -> Option<HeaplessBigInt<T, CAP, P>> {
240 <HeaplessBigInt<T, CAP, P> as CheckedShl>::checked_shl(*self, bits)
241 }
242}
243
244impl<T: MachineWord, const CAP: usize, P: Personality> CheckedShr for &HeaplessBigInt<T, CAP, P> {
245 type Output = HeaplessBigInt<T, CAP, P>;
246 fn checked_shr(self, bits: u32) -> Option<HeaplessBigInt<T, CAP, P>> {
247 <HeaplessBigInt<T, CAP, P> as CheckedShr>::checked_shr(*self, bits)
248 }
249}
250
251impl<T: MachineWord, const CAP: usize, P: Personality> UnboundedShl for &HeaplessBigInt<T, CAP, P> {
252 type Output = HeaplessBigInt<T, CAP, P>;
253 fn unbounded_shl(self, rhs: u32) -> HeaplessBigInt<T, CAP, P> {
254 <HeaplessBigInt<T, CAP, P> as UnboundedShl>::unbounded_shl(*self, rhs)
255 }
256}
257
258impl<T: MachineWord, const CAP: usize, P: Personality> UnboundedShr for &HeaplessBigInt<T, CAP, P> {
259 type Output = HeaplessBigInt<T, CAP, P>;
260 fn unbounded_shr(self, rhs: u32) -> HeaplessBigInt<T, CAP, P> {
261 <HeaplessBigInt<T, CAP, P> as UnboundedShr>::unbounded_shr(*self, rhs)
262 }
263}
264
265impl<T: MachineWord, const CAP: usize, P: Personality> ShlExact for &HeaplessBigInt<T, CAP, P> {
266 type Output = HeaplessBigInt<T, CAP, P>;
267 fn shl_exact(self, rhs: u32) -> Option<HeaplessBigInt<T, CAP, P>> {
268 <HeaplessBigInt<T, CAP, P> as ShlExact>::shl_exact(*self, rhs)
269 }
270}
271
272impl<T: MachineWord, const CAP: usize, P: Personality> ShrExact for &HeaplessBigInt<T, CAP, P> {
273 type Output = HeaplessBigInt<T, CAP, P>;
274 fn shr_exact(self, rhs: u32) -> Option<HeaplessBigInt<T, CAP, P>> {
275 <HeaplessBigInt<T, CAP, P> as ShrExact>::shr_exact(*self, rhs)
276 }
277}
278
279impl<T: MachineWord, const CAP: usize, P: Personality> FunnelShl for &HeaplessBigInt<T, CAP, P> {
280 type Output = HeaplessBigInt<T, CAP, P>;
281 fn funnel_shl(self, rhs: Self, n: u32) -> HeaplessBigInt<T, CAP, P> {
282 <HeaplessBigInt<T, CAP, P> as FunnelShl>::funnel_shl(*self, *rhs, n)
283 }
284}
285
286impl<T: MachineWord, const CAP: usize, P: Personality> FunnelShr for &HeaplessBigInt<T, CAP, P> {
287 type Output = HeaplessBigInt<T, CAP, P>;
288 fn funnel_shr(self, rhs: Self, n: u32) -> HeaplessBigInt<T, CAP, P> {
289 <HeaplessBigInt<T, CAP, P> as FunnelShr>::funnel_shr(*self, *rhs, n)
290 }
291}
292
293#[cfg(feature = "num-traits")]
295impl<T: MachineWord, const CAP: usize, P: Personality> num_traits::WrappingShl
296 for HeaplessBigInt<T, CAP, P>
297{
298 fn wrapping_shl(&self, bits: u32) -> Self {
299 <Self as WrappingShl>::wrapping_shl(*self, bits)
300 }
301}
302
303#[cfg(feature = "num-traits")]
304impl<T: MachineWord, const CAP: usize, P: Personality> num_traits::WrappingShr
305 for HeaplessBigInt<T, CAP, P>
306{
307 fn wrapping_shr(&self, bits: u32) -> Self {
308 <Self as WrappingShr>::wrapping_shr(*self, bits)
309 }
310}
311
312#[cfg(feature = "num-traits")]
313impl<T: MachineWord, const CAP: usize, P: Personality> num_traits::CheckedShl
314 for HeaplessBigInt<T, CAP, P>
315{
316 fn checked_shl(&self, bits: u32) -> Option<Self> {
317 <Self as CheckedShl>::checked_shl(*self, bits)
318 }
319}
320
321#[cfg(feature = "num-traits")]
322impl<T: MachineWord, const CAP: usize, P: Personality> num_traits::CheckedShr
323 for HeaplessBigInt<T, CAP, P>
324{
325 fn checked_shr(&self, bits: u32) -> Option<Self> {
326 <Self as CheckedShr>::checked_shr(*self, bits)
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::HeaplessBigInt;
333 use const_num_traits::{
334 CheckedShl, CheckedShr, FunnelShl, FunnelShr, OverflowingShl, ShlExact, ShrExact,
335 UnboundedShl, UnboundedShr, WrappingShl,
336 };
337
338 type H = HeaplessBigInt<u8, 4>; #[test]
341 fn overflowing_wrapping_checked() {
342 let v = H::from(1u8).widened(4);
343 assert_eq!(
345 OverflowingShl::overflowing_shl(v, 4),
346 (H::from(16u8), false)
347 );
348 assert_eq!(OverflowingShl::overflowing_shl(v, 32), (v, true));
350 assert_eq!(WrappingShl::wrapping_shl(v, 32), v);
351 assert_eq!(CheckedShl::checked_shl(v, 32), None);
352 assert_eq!(CheckedShl::checked_shl(v, 5), Some(H::from(32u8)));
353 assert_eq!(CheckedShr::checked_shr(v, 32), None);
354 }
355
356 #[test]
357 fn unbounded_saturates_to_zero() {
358 let v = H::from(0xFFu8).widened(4);
359 assert_eq!(UnboundedShl::unbounded_shl(v, 100), H::from(0u8));
360 assert_eq!(UnboundedShr::unbounded_shr(v, 100), H::from(0u8));
361 assert_eq!(UnboundedShl::unbounded_shl(v, 100).len(), 4);
363 }
364
365 #[test]
366 fn exact_shifts() {
367 let v = H::from(0b100u8).widened(4);
368 assert_eq!(ShrExact::shr_exact(v, 2), Some(H::from(1u8)));
370 assert_eq!(ShrExact::shr_exact(v, 3), None);
371 assert!(ShlExact::shl_exact(H::from(1u8).widened(4), 31).is_some());
373 assert_eq!(ShlExact::shl_exact(H::from(1u8).widened(4), 32), None);
374 }
375
376 #[test]
378 fn shr_exact_preserves_width() {
379 let v = H::from(256u16).widened(4); let r = ShrExact::shr_exact(v, 8).unwrap();
381 assert_eq!(r, H::from(1u8));
382 assert_eq!(r.len(), 4, "exact shr must not narrow away the width");
383 assert_eq!(r << 8usize, H::from(256u16));
385 }
386
387 #[test]
390 fn u32_operator_shifts_handle_over_width() {
391 let v = H::from(0xFFu8).widened(4);
392 let sl = core::ops::Shl::<u32>::shl(v, 100);
393 assert!(<H as const_num_traits::Zero>::is_zero(&sl));
394 assert_eq!(sl.len(), 4);
395 let sr = core::ops::Shr::<u32>::shr(v, 100);
396 assert!(<H as const_num_traits::Zero>::is_zero(&sr));
397 }
398
399 #[test]
402 fn funnel_zero_shift_on_empty_operand() {
403 let z0 = H::new_zero_with_len(0);
404 assert_eq!(FunnelShl::funnel_shl(z0, z0, 0).len(), 0);
405 assert_eq!(FunnelShr::funnel_shr(z0, z0, 0).len(), 0);
406 }
407
408 #[test]
409 #[should_panic(expected = "must share a width")]
410 fn funnel_rejects_width_mismatch() {
411 let narrow = H::from(1u8); let wide = H::from(1u8).widened(4); FunnelShl::funnel_shl(narrow, wide, 1);
414 }
415
416 #[test]
417 fn funnel() {
418 let hi = H::from(0x1234_5678u32);
421 let lo = H::from(0x9ABC_DEF0u32);
422 assert_eq!(FunnelShl::funnel_shl(hi, lo, 8), H::from(0x3456_789Au32));
424 assert_eq!(FunnelShr::funnel_shr(hi, lo, 8), H::from(0x789A_BCDEu32));
426 assert_eq!(FunnelShl::funnel_shl(hi, lo, 0), hi);
427 assert_eq!(FunnelShr::funnel_shr(hi, lo, 0), lo);
428 }
429
430 #[test]
432 fn by_ref_matches_value() {
433 let v = H::from(1u8).widened(4);
434 assert_eq!(
435 OverflowingShl::overflowing_shl(&v, 4),
436 OverflowingShl::overflowing_shl(v, 4)
437 );
438 assert_eq!(
439 WrappingShl::wrapping_shl(&v, 5),
440 WrappingShl::wrapping_shl(v, 5)
441 );
442 assert_eq!(
443 CheckedShl::checked_shl(&v, 5),
444 CheckedShl::checked_shl(v, 5)
445 );
446 assert_eq!(
447 UnboundedShl::unbounded_shl(&v, 100),
448 UnboundedShl::unbounded_shl(v, 100)
449 );
450 assert_eq!(ShlExact::shl_exact(&v, 3), ShlExact::shl_exact(v, 3));
451 assert_eq!(ShrExact::shr_exact(&v, 0), ShrExact::shr_exact(v, 0));
452
453 let hi = H::from(0x1234_5678u32);
454 let lo = H::from(0x9ABC_DEF0u32);
455 assert_eq!(
456 FunnelShl::funnel_shl(&hi, &lo, 8),
457 FunnelShl::funnel_shl(hi, lo, 8)
458 );
459 assert_eq!(
460 FunnelShr::funnel_shr(&hi, &lo, 8),
461 FunnelShr::funnel_shr(hi, lo, 8)
462 );
463 }
464}