Skip to main content

ruint/
bits.rs

1use crate::{Uint, utils::select_unpredictable_u32};
2use core::ops::{
3    BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, ShlAssign, Shr,
4    ShrAssign,
5};
6
7/// Saturating `usize` → `u32` cast for shift amounts.
8///
9/// The primitive fast paths (`LIMBS ∈ {1, 2, 4}`) feed `rhs` to
10/// `unbounded_sh*`, which take a `u32`. A plain `rhs as u32` cast reduces shift
11/// amounts `>= 2^32` mod `2^32`, shifting by the wrong amount instead of
12/// shifting the whole value out. Saturating to `u32::MAX` keeps any such shift
13/// `>= BITS`, so `unbounded_sh*` returns 0, matching the generic path.
14/// Branchless, and a no-op the compiler elides on 32-bit targets (where `usize`
15/// cannot exceed `u32::MAX`).
16#[inline(always)]
17const fn shift_amount(rhs: usize) -> u32 {
18    select_unpredictable_u32(rhs > u32::MAX as usize, u32::MAX, rhs as u32)
19}
20
21impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
22    /// Returns whether a specific bit is set.
23    ///
24    /// Returns `false` if `index` exceeds the bit width of the number.
25    #[must_use]
26    #[inline]
27    pub const fn bit(&self, index: usize) -> bool {
28        if index >= BITS {
29            return false;
30        }
31        let (limbs, bits) = (index / 64, index % 64);
32        self.limbs[limbs] & (1 << bits) != 0
33    }
34
35    /// Sets a specific bit to a value.
36    #[inline]
37    pub const fn set_bit(&mut self, index: usize, value: bool) {
38        if index >= BITS {
39            return;
40        }
41        let (limbs, bits) = (index / 64, index % 64);
42        if value {
43            self.limbs[limbs] |= 1 << bits;
44        } else {
45            self.limbs[limbs] &= !(1 << bits);
46        }
47    }
48
49    /// Returns a specific byte. The byte at index `0` is the least significant
50    /// byte (little endian).
51    ///
52    /// # Panics
53    ///
54    /// Panics if `index` is greater than or equal to the byte width of the
55    /// number.
56    ///
57    /// # Examples
58    ///
59    /// ```
60    /// # use ruint::uint;
61    /// let x = uint!(0x1234567890_U64);
62    /// let bytes = [
63    ///     x.byte(0), // 0x90
64    ///     x.byte(1), // 0x78
65    ///     x.byte(2), // 0x56
66    ///     x.byte(3), // 0x34
67    ///     x.byte(4), // 0x12
68    ///     x.byte(5), // 0x00
69    ///     x.byte(6), // 0x00
70    ///     x.byte(7), // 0x00
71    /// ];
72    /// assert_eq!(bytes, x.to_le_bytes());
73    /// ```
74    ///
75    /// Panics if out of range.
76    ///
77    /// ```should_panic
78    /// # use ruint::uint;
79    /// let x = uint!(0x1234567890_U64);
80    /// let _ = x.byte(8);
81    /// ```
82    #[inline]
83    #[must_use]
84    #[track_caller]
85    pub const fn byte(&self, index: usize) -> u8 {
86        #[cfg(target_endian = "little")]
87        {
88            self.as_le_slice()[index]
89        }
90
91        #[cfg(target_endian = "big")]
92        #[allow(clippy::cast_possible_truncation)] // intentional
93        {
94            assert!(index < Self::BYTES, "index out of bounds");
95            (self.limbs[index / 8] >> ((index % 8) * 8)) as u8
96        }
97    }
98
99    /// Returns a specific byte, or `None` if `index` is out of range. The byte
100    /// at index `0` is the least significant byte (little endian).
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// # use ruint::uint;
106    /// let x = uint!(0x1234567890_U64);
107    /// assert_eq!(x.checked_byte(0), Some(0x90));
108    /// assert_eq!(x.checked_byte(7), Some(0x00));
109    /// // Out of range
110    /// assert_eq!(x.checked_byte(8), None);
111    /// ```
112    #[inline]
113    #[must_use]
114    pub const fn checked_byte(&self, index: usize) -> Option<u8> {
115        if index < Self::BYTES {
116            Some(self.byte(index))
117        } else {
118            None
119        }
120    }
121
122    /// Reverses the order of bits in the integer. The least significant bit
123    /// becomes the most significant bit, second least-significant bit becomes
124    /// second most-significant bit, etc.
125    #[inline]
126    #[must_use]
127    pub const fn reverse_bits(mut self) -> Self {
128        const_range_for!(i in 0..LIMBS / 2 => {
129            let j = LIMBS - 1 - i;
130            let limb = self.limbs[i];
131            self.limbs[i] = self.limbs[j].reverse_bits();
132            self.limbs[j] = limb.reverse_bits();
133        });
134        if LIMBS % 2 == 1 {
135            let i = LIMBS / 2;
136            self.limbs[i] = self.limbs[i].reverse_bits();
137        }
138        if !BITS.is_multiple_of(64) {
139            self = self.wrapping_shr(64 - BITS % 64);
140        }
141        self
142    }
143
144    /// Inverts all the bits in the integer.
145    #[inline]
146    #[must_use]
147    pub const fn not(mut self) -> Self {
148        if BITS == 0 {
149            return Self::ZERO;
150        }
151        const_range_for!(limb in mut self.limbs => {
152            *limb = !*limb;
153        });
154        self.masked()
155    }
156
157    /// Returns the number of significant words (limbs) in the integer.
158    ///
159    /// If this is 0, then `self` is zero.
160    #[inline]
161    pub(crate) const fn count_significant_words(&self) -> usize {
162        let mut i = LIMBS;
163        while i > 0 {
164            i -= 1;
165            if self.limbs[i] != 0 {
166                return i + 1;
167            }
168        }
169        0
170    }
171
172    /// Returns the number of leading zeros in the binary representation of
173    /// `self`.
174    #[inline]
175    #[must_use]
176    pub const fn leading_zeros(&self) -> usize {
177        let fixed = Self::MASK.leading_zeros() as usize;
178
179        as_primitives!(self; {
180            u64(x) => return x.leading_zeros() as usize - fixed,
181            u128(x) => return x.leading_zeros() as usize - fixed,
182            u256((lo, hi)) => return (select_unpredictable_u32(hi != 0,
183                hi.leading_zeros(),
184                lo.leading_zeros() + 128
185            )) as usize - fixed,
186        });
187
188        let s = self.count_significant_words();
189        if s == 0 {
190            return BITS;
191        }
192        let n = LIMBS - s;
193        let skipped = n * 64;
194        let top = self.limbs[s - 1].leading_zeros() as usize;
195        skipped + top - fixed
196    }
197
198    /// Returns the number of leading ones in the binary representation of
199    /// `self`.
200    #[inline]
201    #[must_use]
202    pub const fn leading_ones(&self) -> usize {
203        let fixed = Self::MASK.leading_zeros() as usize;
204
205        as_primitives!(self; {
206            u64(x) => return (x | !Self::MASK).leading_ones() as usize - fixed,
207            u128(x) => {
208                let mask = (Self::MASK as u128) << 64 | u64::MAX as u128;
209                return (x | !mask).leading_ones() as usize - fixed;
210            },
211            u256((lo, hi)) => {
212                let hi_mask = (Self::MASK as u128) << 64 | u64::MAX as u128;
213                let hi = hi | !hi_mask;
214                let ones = if hi == u128::MAX {
215                    hi.leading_ones() + lo.leading_ones()
216                } else {
217                    hi.leading_ones()
218                };
219                return ones as usize - fixed;
220            },
221        });
222
223        Self::not(*self).leading_zeros()
224    }
225
226    /// Returns the number of trailing zeros in the binary representation of
227    /// `self`.
228    #[inline]
229    #[must_use]
230    pub const fn trailing_zeros(&self) -> usize {
231        as_primitives!(self; {
232            u64(x) => {
233                let zeros = x.trailing_zeros() as usize;
234                return if zeros > BITS { BITS } else { zeros };
235            },
236            u128(x) => {
237                let zeros = x.trailing_zeros() as usize;
238                return if zeros > BITS { BITS } else { zeros };
239            },
240            u256((lo, hi)) => {
241                let zeros = if lo == 0 {
242                    hi.trailing_zeros() + 128
243                } else {
244                    lo.trailing_zeros()
245                } as usize;
246                return if zeros > BITS { BITS } else { zeros };
247            },
248        });
249
250        const_range_for!(i in 0..LIMBS => {
251            if self.limbs[i] != 0 {
252                return i * 64 + self.limbs[i].trailing_zeros() as usize;
253            }
254        });
255        BITS
256    }
257
258    /// Returns the number of trailing ones in the binary representation of
259    /// `self`.
260    #[inline]
261    #[must_use]
262    pub const fn trailing_ones(&self) -> usize {
263        as_primitives!(self; {
264            u64(x) => return x.trailing_ones() as usize,
265            u128(x) => return x.trailing_ones() as usize,
266            u256((lo, hi)) => return if lo == u128::MAX {
267                (hi.trailing_ones() + 128) as usize
268            } else {
269                lo.trailing_ones() as usize
270            },
271        });
272
273        const_range_for!(i in 0..LIMBS => {
274            if self.limbs[i] != u64::MAX {
275                return i * 64 + self.limbs[i].trailing_ones() as usize;
276            }
277        });
278        BITS
279    }
280
281    /// Returns the number of ones in the binary representation of `self`.
282    #[inline]
283    #[must_use]
284    pub const fn count_ones(&self) -> usize {
285        let mut ones = 0;
286        const_range_for!(limb in ref self.as_limbs() => {
287            ones += limb.count_ones() as usize;
288        });
289        ones
290    }
291
292    /// Returns the number of zeros in the binary representation of `self`.
293    #[must_use]
294    #[inline]
295    pub const fn count_zeros(&self) -> usize {
296        BITS - self.count_ones()
297    }
298
299    /// Returns the dynamic length of this number in bits, ignoring leading
300    /// zeros.
301    ///
302    /// For the maximum length of the type, use [`Uint::BITS`](Self::BITS).
303    #[must_use]
304    #[inline]
305    pub const fn bit_len(&self) -> usize {
306        BITS - self.leading_zeros()
307    }
308
309    /// Returns the dynamic length of this number in bytes, ignoring leading
310    /// zeros.
311    ///
312    /// For the maximum length of the type, use [`Uint::BYTES`](Self::BYTES).
313    #[must_use]
314    #[inline]
315    pub const fn byte_len(&self) -> usize {
316        self.bit_len().div_ceil(8)
317    }
318
319    /// Returns the most significant 64 bits of the number and the exponent.
320    ///
321    /// Given return value $(\mathtt{bits}, \mathtt{exponent})$, the `self` can
322    /// be approximated as
323    ///
324    /// $$
325    /// \mathtt{self} ≈ \mathtt{bits} ⋅ 2^\mathtt{exponent}
326    /// $$
327    ///
328    /// If `self` is $<≥> 2^{63}$, then `exponent` will be zero and `bits` will
329    /// have leading zeros.
330    #[inline]
331    #[must_use]
332    pub const fn most_significant_bits(&self) -> (u64, usize) {
333        let significant_words = self.count_significant_words();
334        if significant_words == 0 {
335            (0, 0)
336        } else if significant_words == 1 {
337            (self.limbs[0], 0)
338        } else {
339            let i = significant_words - 1;
340            let hi = self.limbs[i];
341            let lo = self.limbs[i - 1];
342            let leading_zeros = hi.leading_zeros();
343            let bits = if leading_zeros > 0 {
344                (hi << leading_zeros) | (lo >> (64 - leading_zeros))
345            } else {
346                hi
347            };
348            let exponent = i * 64 - leading_zeros as usize;
349            (bits, exponent)
350        }
351    }
352
353    /// Checked left shift by `rhs` bits.
354    ///
355    /// Returns $\mathtt{self} ⋅ 2^{\mathtt{rhs}}$ or [`None`] if the result
356    /// would $≥ 2^{\mathtt{BITS}}$. That is, it returns [`None`] if the bits
357    /// shifted out would be non-zero.
358    ///
359    /// Note: This differs from [`u64::checked_shl`] which returns `None` if the
360    /// shift is larger than BITS (which is IMHO not very useful).
361    #[inline(always)]
362    #[must_use]
363    pub const fn checked_shl(self, rhs: usize) -> Option<Self> {
364        match self.overflowing_shl(rhs) {
365            (value, false) => Some(value),
366            _ => None,
367        }
368    }
369
370    /// Left shift by `rhs` bits, panicking if the bits shifted out are
371    /// non-zero.
372    ///
373    /// Note: This differs from [`u64::strict_shl`] which panics if the shift is
374    /// larger than `BITS`.
375    ///
376    /// # Panics
377    ///
378    /// This function will always panic on overflow, regardless of whether
379    /// overflow checks are enabled.
380    #[inline(always)]
381    #[must_use]
382    #[track_caller]
383    pub const fn strict_shl(self, rhs: usize) -> Self {
384        match self.overflowing_shl(rhs) {
385            (value, false) => value,
386            _ => panic!("attempt to shift left with overflow"),
387        }
388    }
389
390    /// Saturating left shift by `rhs` bits.
391    ///
392    /// Returns $\mathtt{self} ⋅ 2^{\mathtt{rhs}}$ or [`Uint::MAX`] if the
393    /// result would $≥ 2^{\mathtt{BITS}}$. That is, it returns
394    /// [`Uint::MAX`] if the bits shifted out would be non-zero.
395    #[inline(always)]
396    #[must_use]
397    pub const fn saturating_shl(self, rhs: usize) -> Self {
398        match self.overflowing_shl(rhs) {
399            (value, false) => value,
400            _ => Self::MAX,
401        }
402    }
403
404    /// Left shift by `rhs` bits with overflow detection.
405    ///
406    /// Returns $\mod{\mathtt{value} ⋅ 2^{\mathtt{rhs}}}_{2^{\mathtt{BITS}}}$.
407    /// If the product is $≥ 2^{\mathtt{BITS}}$ it returns `true`. That is, it
408    /// returns true if the bits shifted out are non-zero.
409    ///
410    /// Note: This differs from [`u64::overflowing_shl`] which returns `true` if
411    /// the shift is larger than `BITS` (which is IMHO not very useful).
412    #[inline]
413    #[must_use]
414    pub const fn overflowing_shl(self, rhs: usize) -> (Self, bool) {
415        let (limbs, bits) = (rhs / 64, rhs % 64);
416        if limbs >= LIMBS {
417            return (Self::ZERO, !self.const_is_zero());
418        }
419        let mut r = Self::ZERO;
420        let bits = bits as u32;
421
422        let mut carry = 0;
423        // check the limbs that are entirely shifted out.
424        const_range_for!(i in 0..LIMBS - limbs => {
425            let x = self.limbs[i];
426            r.limbs[i + limbs] = (x << bits) | carry;
427            carry = x.unbounded_shr(64 - bits);
428        });
429        // we need to know whether any limb entirely shifted out is non-zero
430        const_range_for!(i in (LIMBS - limbs)..LIMBS => {
431            carry |= self.limbs[i];
432        });
433        // we also need to know if the top limb is dirty before masking
434        carry |= r.maskable_bits();
435        (r.masked(), carry != 0)
436    }
437
438    /// Left shift by `rhs` bits with overflow detection, but with `Self` rhs.
439    ///
440    /// See [`overflowing_shl`](Self::overflowing_shl) for details.
441    #[inline]
442    pub(crate) fn overflowing_shl_big(self, rhs: Self) -> (Self, bool) {
443        if BITS == 0 {
444            return (Self::ZERO, false);
445        }
446        // A shift amount that doesn't fit `usize` is `> usize::MAX >= BITS`,
447        // so the entire value is shifted out. The conversion is
448        // pointer-width-aware, so shift amounts in `[2^32, 2^64)` cannot
449        // truncate on 32-bit targets (where `usize` is narrower than `u64`).
450        let Ok(rhs) = usize::try_from(rhs) else {
451            return (Self::ZERO, !self.const_is_zero());
452        };
453        self.overflowing_shl(rhs)
454    }
455
456    /// Left shift by `rhs` bits.
457    ///
458    /// Returns $\mod{\mathtt{value} ⋅ 2^{\mathtt{rhs}}}_{2^{\mathtt{BITS}}}$.
459    ///
460    /// Note: This differs from [`u64::wrapping_shl`] which first reduces `rhs`
461    /// by `BITS` (which is IMHO not very useful).
462    #[inline(always)]
463    #[must_use]
464    pub const fn wrapping_shl(self, rhs: usize) -> Self {
465        as_primitives!(self; {
466            u64(x) => {
467                let mut r = Self::ZERO;
468                r.limbs[0] = x.unbounded_shl(shift_amount(rhs));
469                return r.masked();
470            },
471            u128(x) => {
472                let r = x.unbounded_shl(shift_amount(rhs));
473                let mut out = Self::ZERO;
474                out.limbs[0] = r as u64;
475                out.limbs[1] = (r >> 64) as u64;
476                return out.masked();
477            },
478            u256((lo, hi)) => {
479                let rhs = shift_amount(rhs);
480                // Compute as if rhs < 128.
481                let new_lo = lo.unbounded_shl(rhs);
482                let new_hi = hi.unbounded_shl(rhs) | lo.unbounded_shr(128u32.wrapping_sub(rhs));
483                // If rhs >= 128, lo becomes 0 and hi becomes lo << (rhs - 128).
484                let cross = lo.unbounded_shl(rhs.wrapping_sub(128));
485                let mask = 0u128.wrapping_sub((rhs < 128) as u128);
486                let lo = new_lo & mask;
487                let hi = (new_hi & mask) | (cross & !mask);
488                let mut r = Self::ZERO;
489                r.limbs[0] = lo as u64;
490                r.limbs[1] = (lo >> 64) as u64;
491                r.limbs[2] = hi as u64;
492                r.limbs[3] = (hi >> 64) as u64;
493                return r.masked();
494            },
495        });
496
497        self.overflowing_shl(rhs).0
498    }
499
500    /// Checked right shift by `rhs` bits.
501    ///
502    /// $$
503    /// \frac{\mathtt{self}}{2^{\mathtt{rhs}}}
504    /// $$
505    ///
506    /// Returns the above or [`None`] if the division is not exact. This is the
507    /// same as
508    ///
509    /// Note: This differs from [`u64::checked_shr`] which returns `None` if the
510    /// shift is larger than BITS (which is IMHO not very useful).
511    #[inline(always)]
512    #[must_use]
513    pub const fn checked_shr(self, rhs: usize) -> Option<Self> {
514        match self.overflowing_shr(rhs) {
515            (value, false) => Some(value),
516            _ => None,
517        }
518    }
519
520    /// Right shift by `rhs` bits, panicking if the bits shifted out are
521    /// non-zero.
522    ///
523    /// Note: This differs from [`u64::strict_shr`] which panics if the shift is
524    /// larger than `BITS`.
525    ///
526    /// # Panics
527    ///
528    /// This function will always panic on overflow, regardless of whether
529    /// overflow checks are enabled.
530    #[inline(always)]
531    #[must_use]
532    #[track_caller]
533    pub const fn strict_shr(self, rhs: usize) -> Self {
534        match self.overflowing_shr(rhs) {
535            (value, false) => value,
536            _ => panic!("attempt to shift right with overflow"),
537        }
538    }
539
540    /// Right shift by `rhs` bits with underflow detection.
541    ///
542    /// $$
543    /// \floor{\frac{\mathtt{self}}{2^{\mathtt{rhs}}}}
544    /// $$
545    ///
546    /// Returns the above and `false` if the division was exact, and `true` if
547    /// it was rounded down. This is the same as non-zero bits being shifted
548    /// out.
549    ///
550    /// Note: This differs from [`u64::overflowing_shr`] which returns `true` if
551    /// the shift is larger than `BITS` (which is IMHO not very useful).
552    #[inline]
553    #[must_use]
554    pub const fn overflowing_shr(self, rhs: usize) -> (Self, bool) {
555        let (limbs, bits) = (rhs / 64, rhs % 64);
556        if limbs >= LIMBS {
557            return (Self::ZERO, !self.const_is_zero());
558        }
559        let mut r = Self::ZERO;
560        let bits = bits as u32;
561
562        let mut carry = 0;
563        // check the limbs that are entirely shifted out.
564        const_range_for!(i in 0..LIMBS - limbs => {
565            let x = self.limbs[LIMBS - 1 - i];
566            r.limbs[LIMBS - 1 - i - limbs] = (x >> bits) | carry;
567            carry = x.unbounded_shl(64 - bits);
568        });
569        // we need to know if any limb entirely shifted out is non-zero
570        const_range_for!(i in 0..limbs => {
571            carry |= self.limbs[i];
572        });
573        (r, carry != 0)
574    }
575
576    /// Right shift by `rhs` bits with underflow detection, but with `Self` rhs.
577    ///
578    /// See [`overflowing_shr`](Self::overflowing_shr) for details.
579    #[inline]
580    pub(crate) fn overflowing_shr_big(self, rhs: Self) -> (Self, bool) {
581        if BITS == 0 {
582            return (Self::ZERO, false);
583        }
584        // A shift amount that doesn't fit `usize` is `> usize::MAX >= BITS`,
585        // so the entire value is shifted out. The conversion is
586        // pointer-width-aware, so shift amounts in `[2^32, 2^64)` cannot
587        // truncate on 32-bit targets (where `usize` is narrower than `u64`).
588        let Ok(rhs) = usize::try_from(rhs) else {
589            return (Self::ZERO, !self.const_is_zero());
590        };
591        self.overflowing_shr(rhs)
592    }
593
594    /// Right shift by `rhs` bits.
595    ///
596    /// $$
597    /// \mathtt{wrapping\\_shr}(\mathtt{self}, \mathtt{rhs}) =
598    /// \floor{\frac{\mathtt{self}}{2^{\mathtt{rhs}}}}
599    /// $$
600    ///
601    /// Note: This differs from [`u64::wrapping_shr`] which first reduces `rhs`
602    /// by `BITS` (which is IMHO not very useful).
603    #[inline(always)]
604    #[must_use]
605    pub const fn wrapping_shr(self, rhs: usize) -> Self {
606        as_primitives!(self; {
607            u64(x) => {
608                let mut r = Self::ZERO;
609                r.limbs[0] = x.unbounded_shr(shift_amount(rhs));
610                return r;
611            },
612            u128(x) => {
613                let r = x.unbounded_shr(shift_amount(rhs));
614                let mut out = Self::ZERO;
615                out.limbs[0] = r as u64;
616                out.limbs[1] = (r >> 64) as u64;
617                return out;
618            },
619            u256((lo, hi)) => {
620                let rhs = shift_amount(rhs);
621                // Compute as if rhs < 128.
622                let new_hi = hi.unbounded_shr(rhs);
623                let new_lo = lo.unbounded_shr(rhs) | hi.unbounded_shl(128u32.wrapping_sub(rhs));
624                // If rhs >= 128, hi becomes 0 and lo becomes hi >> (rhs - 128).
625                let cross = hi.unbounded_shr(rhs.wrapping_sub(128));
626                let mask = 0u128.wrapping_sub((rhs < 128) as u128);
627                let hi = new_hi & mask;
628                let lo = (new_lo & mask) | (cross & !mask);
629                let mut r = Self::ZERO;
630                r.limbs[0] = lo as u64;
631                r.limbs[1] = (lo >> 64) as u64;
632                r.limbs[2] = hi as u64;
633                r.limbs[3] = (hi >> 64) as u64;
634                return r;
635            },
636        });
637
638        self.overflowing_shr(rhs).0
639    }
640
641    /// Arithmetic shift right by `rhs` bits.
642    #[inline]
643    #[must_use]
644    pub const fn arithmetic_shr(self, rhs: usize) -> Self {
645        if BITS == 0 {
646            return Self::ZERO;
647        }
648        let sign = self.bit(BITS - 1);
649        let mut r = self.wrapping_shr(rhs);
650        if sign {
651            // r |= Self::MAX << BITS.saturating_sub(rhs);
652            r = r.bitor(Self::MAX.wrapping_shl(BITS.saturating_sub(rhs)));
653        }
654        r
655    }
656
657    /// Shifts the bits to the left by a specified amount, `rhs`, wrapping the
658    /// truncated bits to the end of the resulting integer.
659    #[inline]
660    #[must_use]
661    pub const fn rotate_left(self, rhs: usize) -> Self {
662        if BITS == 0 {
663            return Self::ZERO;
664        }
665        let rhs = rhs % BITS;
666        // (self << rhs) | (self >> (BITS - rhs))
667        self.wrapping_shl(rhs).bitor(self.wrapping_shr(BITS - rhs))
668    }
669
670    /// Shifts the bits to the right by a specified amount, `rhs`, wrapping the
671    /// truncated bits to the beginning of the resulting integer.
672    #[inline(always)]
673    #[must_use]
674    pub const fn rotate_right(self, rhs: usize) -> Self {
675        if BITS == 0 {
676            return Self::ZERO;
677        }
678        let rhs = rhs % BITS;
679        self.rotate_left(BITS - rhs)
680    }
681}
682
683impl<const BITS: usize, const LIMBS: usize> Not for Uint<BITS, LIMBS> {
684    type Output = Self;
685
686    #[inline]
687    fn not(self) -> Self::Output {
688        self.not()
689    }
690}
691
692impl<const BITS: usize, const LIMBS: usize> Not for &Uint<BITS, LIMBS> {
693    type Output = Uint<BITS, LIMBS>;
694
695    #[inline]
696    fn not(self) -> Self::Output {
697        (*self).not()
698    }
699}
700
701macro_rules! impl_bit_op {
702    ($op:tt, $assign_op:tt, $trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => {
703        impl<const BITS: usize, const LIMBS: usize> $trait_assign<Uint<BITS, LIMBS>>
704            for Uint<BITS, LIMBS>
705        {
706            #[inline(always)]
707            fn $fn_assign(&mut self, rhs: Uint<BITS, LIMBS>) {
708                self.$fn_assign(&rhs);
709            }
710        }
711
712        impl<const BITS: usize, const LIMBS: usize> $trait_assign<&Uint<BITS, LIMBS>>
713            for Uint<BITS, LIMBS>
714        {
715            #[inline]
716            fn $fn_assign(&mut self, rhs: &Uint<BITS, LIMBS>) {
717                for i in 0..LIMBS {
718                    u64::$fn_assign(&mut self.limbs[i], rhs.limbs[i]);
719                }
720            }
721        }
722
723        impl<const BITS: usize, const LIMBS: usize> $trait<Uint<BITS, LIMBS>>
724            for Uint<BITS, LIMBS>
725        {
726            type Output = Uint<BITS, LIMBS>;
727
728            #[inline(always)]
729            fn $fn(mut self, rhs: Uint<BITS, LIMBS>) -> Self::Output {
730                self.$fn_assign(rhs);
731                self
732            }
733        }
734
735        impl<const BITS: usize, const LIMBS: usize> $trait<&Uint<BITS, LIMBS>>
736            for Uint<BITS, LIMBS>
737        {
738            type Output = Uint<BITS, LIMBS>;
739
740            #[inline(always)]
741            fn $fn(mut self, rhs: &Uint<BITS, LIMBS>) -> Self::Output {
742                self.$fn_assign(rhs);
743                self
744            }
745        }
746
747        impl<const BITS: usize, const LIMBS: usize> $trait<Uint<BITS, LIMBS>>
748            for &Uint<BITS, LIMBS>
749        {
750            type Output = Uint<BITS, LIMBS>;
751
752            #[inline(always)]
753            fn $fn(self, mut rhs: Uint<BITS, LIMBS>) -> Self::Output {
754                rhs.$fn_assign(self);
755                rhs
756            }
757        }
758
759        impl<const BITS: usize, const LIMBS: usize> $trait<&Uint<BITS, LIMBS>>
760            for &Uint<BITS, LIMBS>
761        {
762            type Output = Uint<BITS, LIMBS>;
763
764            #[inline(always)]
765            fn $fn(self, rhs: &Uint<BITS, LIMBS>) -> Self::Output {
766                <Uint<BITS, LIMBS>>::$fn(*self, *rhs)
767            }
768        }
769
770        impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
771            #[doc = concat!("Returns the bitwise `", stringify!($op), "` of the two numbers.")]
772            #[inline(always)]
773            #[must_use]
774            pub const fn $fn(mut self, rhs: Uint<BITS, LIMBS>) -> Uint<BITS, LIMBS> {
775                const_range_for!(i in 0..LIMBS => {
776                    self.limbs[i] $assign_op rhs.limbs[i];
777                });
778                self
779            }
780        }
781    };
782}
783
784impl_bit_op!(|, |=, BitOr,  bitor,  BitOrAssign,  bitor_assign);
785impl_bit_op!(&, &=, BitAnd, bitand, BitAndAssign, bitand_assign);
786impl_bit_op!(^, ^=, BitXor, bitxor, BitXorAssign, bitxor_assign);
787
788impl<const BITS: usize, const LIMBS: usize> Shl<Self> for Uint<BITS, LIMBS> {
789    type Output = Self;
790
791    #[inline(always)]
792    fn shl(self, rhs: Self) -> Self::Output {
793        self.overflowing_shl_big(rhs).0
794    }
795}
796
797impl<const BITS: usize, const LIMBS: usize> Shl<&Self> for Uint<BITS, LIMBS> {
798    type Output = Self;
799
800    #[inline(always)]
801    fn shl(self, rhs: &Self) -> Self::Output {
802        self << *rhs
803    }
804}
805
806impl<const BITS: usize, const LIMBS: usize> Shr<Self> for Uint<BITS, LIMBS> {
807    type Output = Self;
808
809    #[inline(always)]
810    fn shr(self, rhs: Self) -> Self::Output {
811        self.overflowing_shr_big(rhs).0
812    }
813}
814
815impl<const BITS: usize, const LIMBS: usize> Shr<&Self> for Uint<BITS, LIMBS> {
816    type Output = Self;
817
818    #[inline(always)]
819    fn shr(self, rhs: &Self) -> Self::Output {
820        self >> *rhs
821    }
822}
823
824impl<const BITS: usize, const LIMBS: usize> ShlAssign<Self> for Uint<BITS, LIMBS> {
825    #[inline(always)]
826    fn shl_assign(&mut self, rhs: Self) {
827        *self = *self << rhs;
828    }
829}
830
831impl<const BITS: usize, const LIMBS: usize> ShlAssign<&Self> for Uint<BITS, LIMBS> {
832    #[inline(always)]
833    fn shl_assign(&mut self, rhs: &Self) {
834        *self = *self << rhs;
835    }
836}
837
838impl<const BITS: usize, const LIMBS: usize> ShrAssign<Self> for Uint<BITS, LIMBS> {
839    #[inline(always)]
840    fn shr_assign(&mut self, rhs: Self) {
841        *self = *self >> rhs;
842    }
843}
844
845impl<const BITS: usize, const LIMBS: usize> ShrAssign<&Self> for Uint<BITS, LIMBS> {
846    #[inline(always)]
847    fn shr_assign(&mut self, rhs: &Self) {
848        *self = *self >> rhs;
849    }
850}
851
852macro_rules! impl_shift {
853    (@main $u:ty) => {
854        impl<const BITS: usize, const LIMBS: usize> Shl<$u> for Uint<BITS, LIMBS> {
855            type Output = Self;
856
857            #[inline(always)]
858            #[allow(clippy::cast_possible_truncation)]
859            fn shl(self, rhs: $u) -> Self::Output {
860                self.wrapping_shl(rhs as usize)
861            }
862        }
863
864        impl<const BITS: usize, const LIMBS: usize> Shr<$u> for Uint<BITS, LIMBS> {
865            type Output = Self;
866
867            #[inline(always)]
868            #[allow(clippy::cast_possible_truncation)]
869            fn shr(self, rhs: $u) -> Self::Output {
870                self.wrapping_shr(rhs as usize)
871            }
872        }
873    };
874
875    (@ref $u:ty) => {
876        impl<const BITS: usize, const LIMBS: usize> Shl<&$u> for Uint<BITS, LIMBS> {
877            type Output = Self;
878
879            #[inline(always)]
880            fn shl(self, rhs: &$u) -> Self::Output {
881                <Self>::shl(self, *rhs)
882            }
883        }
884
885        impl<const BITS: usize, const LIMBS: usize> Shr<&$u> for Uint<BITS, LIMBS> {
886            type Output = Self;
887
888            #[inline(always)]
889            fn shr(self, rhs: &$u) -> Self::Output {
890                <Self>::shr(self, *rhs)
891            }
892        }
893    };
894
895    (@assign $u:ty) => {
896        impl<const BITS: usize, const LIMBS: usize> ShlAssign<$u> for Uint<BITS, LIMBS> {
897            #[inline(always)]
898            fn shl_assign(&mut self, rhs: $u) {
899                *self = *self << rhs;
900            }
901        }
902
903        impl<const BITS: usize, const LIMBS: usize> ShrAssign<$u> for Uint<BITS, LIMBS> {
904            #[inline(always)]
905            fn shr_assign(&mut self, rhs: $u) {
906                *self = *self >> rhs;
907            }
908        }
909    };
910
911    ($u:ty) => {
912        impl_shift!(@main $u);
913        impl_shift!(@ref $u);
914        impl_shift!(@assign $u);
915        impl_shift!(@assign &$u);
916    };
917
918    ($u:ty, $($tail:ty),*) => {
919        impl_shift!($u);
920        impl_shift!($($tail),*);
921    };
922}
923
924impl_shift!(usize, u8, u16, u32, isize, i8, i16, i32);
925
926// Only when losslessy castable to usize.
927#[cfg(target_pointer_width = "64")]
928impl_shift!(u64, i64);
929
930#[cfg(test)]
931mod tests {
932    use super::*;
933    use crate::{
934        aliases::{U128, U256},
935        const_for, nlimbs,
936    };
937    use core::cmp::min;
938    use proptest::proptest;
939
940    fn reference_leading_zeros<const BITS: usize, const LIMBS: usize>(
941        value: Uint<BITS, LIMBS>,
942    ) -> usize {
943        let mut zeros = 0;
944        while zeros < BITS && !value.bit(BITS - zeros - 1) {
945            zeros += 1;
946        }
947        zeros
948    }
949
950    fn reference_leading_ones<const BITS: usize, const LIMBS: usize>(
951        value: Uint<BITS, LIMBS>,
952    ) -> usize {
953        let mut ones = 0;
954        while ones < BITS && value.bit(BITS - ones - 1) {
955            ones += 1;
956        }
957        ones
958    }
959
960    fn reference_trailing_zeros<const BITS: usize, const LIMBS: usize>(
961        value: Uint<BITS, LIMBS>,
962    ) -> usize {
963        let mut zeros = 0;
964        while zeros < BITS && !value.bit(zeros) {
965            zeros += 1;
966        }
967        zeros
968    }
969
970    fn reference_trailing_ones<const BITS: usize, const LIMBS: usize>(
971        value: Uint<BITS, LIMBS>,
972    ) -> usize {
973        let mut ones = 0;
974        while ones < BITS && value.bit(ones) {
975            ones += 1;
976        }
977        ones
978    }
979
980    #[test]
981    fn test_leading_zeros() {
982        assert_eq!(Uint::<0, 0>::ZERO.leading_zeros(), 0);
983        const_for!(BITS in NON_ZERO {
984            const LIMBS: usize = nlimbs(BITS);
985            type U = Uint::<BITS, LIMBS>;
986            assert_eq!(U::ZERO.leading_zeros(), BITS);
987            assert_eq!(U::MAX.leading_zeros(), 0);
988            assert_eq!(U::ONE.leading_zeros(), BITS - 1);
989            proptest!(|(value: U)| {
990                assert_eq!(value.leading_zeros(), reference_leading_zeros(value));
991            });
992        });
993
994        assert_eq!(
995            U256::from_limbs([1, 0, 0, 0]).leading_zeros(),
996            reference_leading_zeros(U256::from_limbs([1, 0, 0, 0]))
997        );
998        assert_eq!(
999            U256::from_limbs([0, 0, 1, 0]).leading_zeros(),
1000            reference_leading_zeros(U256::from_limbs([0, 0, 1, 0]))
1001        );
1002    }
1003
1004    #[test]
1005    fn test_leading_ones() {
1006        assert_eq!(Uint::<0, 0>::ZERO.leading_ones(), 0);
1007        const_for!(BITS in NON_ZERO {
1008            const LIMBS: usize = nlimbs(BITS);
1009            type U = Uint::<BITS, LIMBS>;
1010            assert_eq!(U::ZERO.leading_ones(), 0);
1011            assert_eq!(U::MAX.leading_ones(), BITS);
1012            assert_eq!((U::MAX << 1_usize).leading_ones(), BITS - 1);
1013            proptest!(|(value: U)| {
1014                assert_eq!(value.leading_ones(), reference_leading_ones(value));
1015            });
1016        });
1017
1018        assert_eq!(
1019            U256::from_limbs([u64::MAX, u64::MAX, u64::MAX, u64::MAX - 1]).leading_ones(),
1020            reference_leading_ones(U256::from_limbs([
1021                u64::MAX,
1022                u64::MAX,
1023                u64::MAX,
1024                u64::MAX - 1,
1025            ]))
1026        );
1027        assert_eq!(
1028            U256::from_limbs([0, 0, u64::MAX, u64::MAX]).leading_ones(),
1029            reference_leading_ones(U256::from_limbs([0, 0, u64::MAX, u64::MAX]))
1030        );
1031    }
1032
1033    #[test]
1034    fn test_trailing_zeros() {
1035        assert_eq!(Uint::<0, 0>::ZERO.trailing_zeros(), 0);
1036        const_for!(BITS in NON_ZERO {
1037            const LIMBS: usize = nlimbs(BITS);
1038            type U = Uint::<BITS, LIMBS>;
1039            assert_eq!(U::ZERO.trailing_zeros(), BITS);
1040            assert_eq!(U::MAX.trailing_zeros(), 0);
1041            assert_eq!((U::MAX << 1_usize).trailing_zeros(), 1);
1042            proptest!(|(value: U)| {
1043                assert_eq!(value.trailing_zeros(), reference_trailing_zeros(value));
1044            });
1045        });
1046
1047        assert_eq!(
1048            U256::from_limbs([0, 0, 1, 0]).trailing_zeros(),
1049            reference_trailing_zeros(U256::from_limbs([0, 0, 1, 0]))
1050        );
1051    }
1052
1053    #[test]
1054    fn test_trailing_ones() {
1055        assert_eq!(Uint::<0, 0>::ZERO.trailing_ones(), 0);
1056        const_for!(BITS in NON_ZERO {
1057            const LIMBS: usize = nlimbs(BITS);
1058            type U = Uint::<BITS, LIMBS>;
1059            assert_eq!(U::ZERO.trailing_ones(), 0);
1060            assert_eq!(U::MAX.trailing_ones(), BITS);
1061            assert_eq!((U::MAX << 1_usize).trailing_ones(), 0);
1062            proptest!(|(value: U)| {
1063                assert_eq!(value.trailing_ones(), reference_trailing_ones(value));
1064            });
1065        });
1066
1067        assert_eq!(
1068            U256::from_limbs([u64::MAX, u64::MAX, 0, 0]).trailing_ones(),
1069            reference_trailing_ones(U256::from_limbs([u64::MAX, u64::MAX, 0, 0]))
1070        );
1071    }
1072
1073    #[test]
1074    fn test_most_significant_bits() {
1075        const_for!(BITS in NON_ZERO {
1076            const LIMBS: usize = nlimbs(BITS);
1077            type U = Uint::<BITS, LIMBS>;
1078            proptest!(|(value: u64)| {
1079                let value = if U::LIMBS <= 1 { value & U::MASK } else { value };
1080                assert_eq!(U::from(value).most_significant_bits(), (value, 0));
1081            });
1082        });
1083        proptest!(|(mut limbs: [u64; 2])| {
1084            if limbs[1] == 0 {
1085                limbs[1] = 1;
1086            }
1087            let (bits, exponent) = U128::from_limbs(limbs).most_significant_bits();
1088            assert!(bits >= 1_u64 << 63);
1089            assert_eq!(exponent, 64 - limbs[1].leading_zeros() as usize);
1090        });
1091    }
1092
1093    #[test]
1094    fn test_checked_shl() {
1095        assert_eq!(
1096            Uint::<65, 2>::from_limbs([0x0010_0000_0000_0000, 0]).checked_shl(1),
1097            Some(Uint::<65, 2>::from_limbs([0x0020_0000_0000_0000, 0]))
1098        );
1099        assert_eq!(
1100            Uint::<127, 2>::from_limbs([0x0010_0000_0000_0000, 0]).checked_shl(64),
1101            Some(Uint::<127, 2>::from_limbs([0, 0x0010_0000_0000_0000]))
1102        );
1103    }
1104
1105    #[test]
1106    #[allow(
1107        clippy::cast_lossless,
1108        clippy::cast_possible_truncation,
1109        clippy::cast_possible_wrap
1110    )]
1111    fn test_small() {
1112        const_for!(BITS in [1, 2, 8, 16, 32, 63, 64] {
1113            type U = Uint::<BITS, 1>;
1114            proptest!(|(a: U, b: U)| {
1115                assert_eq!(a | b, U::from_limbs([a.limbs[0] | b.limbs[0]]));
1116                assert_eq!(a & b, U::from_limbs([a.limbs[0] & b.limbs[0]]));
1117                assert_eq!(a ^ b, U::from_limbs([a.limbs[0] ^ b.limbs[0]]));
1118            });
1119            proptest!(|(a: U, s in 0..BITS)| {
1120                assert_eq!(a << s, U::from_limbs([a.limbs[0] << s & U::MASK]));
1121                assert_eq!(a >> s, U::from_limbs([a.limbs[0] >> s]));
1122            });
1123        });
1124        proptest!(|(a: Uint::<32, 1>, s in 0_usize..=34)| {
1125            assert_eq!(a.reverse_bits(), Uint::from((a.limbs[0] as u32).reverse_bits() as u64));
1126            assert_eq!(a.rotate_left(s), Uint::from((a.limbs[0] as u32).rotate_left(s as u32) as u64));
1127            assert_eq!(a.rotate_right(s), Uint::from((a.limbs[0] as u32).rotate_right(s as u32) as u64));
1128            if s < 32 {
1129                let arr_shifted = (((a.limbs[0] as i32) >> s) as u32) as u64;
1130                assert_eq!(a.arithmetic_shr(s), Uint::from_limbs([arr_shifted]));
1131            }
1132        });
1133        proptest!(|(a: Uint::<64, 1>, s in 0_usize..=66)| {
1134            assert_eq!(a.reverse_bits(), Uint::from(a.limbs[0].reverse_bits()));
1135            assert_eq!(a.rotate_left(s), Uint::from(a.limbs[0].rotate_left(s as u32)));
1136            assert_eq!(a.rotate_right(s), Uint::from(a.limbs[0].rotate_right(s as u32)));
1137            if s < 64 {
1138                let arr_shifted = ((a.limbs[0] as i64) >> s) as u64;
1139                assert_eq!(a.arithmetic_shr(s), Uint::from_limbs([arr_shifted]));
1140            }
1141        });
1142    }
1143
1144    #[test]
1145    #[allow(clippy::absurd_extreme_comparisons)] // Generated code
1146    fn test_const_reverse_and_most_significant_bits() {
1147        const_for!(BITS in SIZES {
1148            const LIMBS: usize = nlimbs(BITS);
1149            type U = Uint<BITS, LIMBS>;
1150            const {
1151                assert!(U::MAX.reverse_bits().const_eq(&U::MAX));
1152                let reversed_one = U::ONE.reverse_bits();
1153                let expected = if BITS == 0 { U::ZERO } else { U::ONE.wrapping_shl(BITS - 1) };
1154                assert!(reversed_one.const_eq(&expected));
1155
1156                let (bits, exponent) = U::MAX.most_significant_bits();
1157                if BITS <= 64 {
1158                    assert!(bits == U::MASK);
1159                    assert!(exponent == 0);
1160                } else {
1161                    assert!(bits == u64::MAX);
1162                    assert!(exponent == BITS - 64);
1163                }
1164            }
1165        });
1166    }
1167
1168    #[test]
1169    fn test_shift_reverse() {
1170        const_for!(BITS in SIZES {
1171            const LIMBS: usize = nlimbs(BITS);
1172            type U = Uint::<BITS, LIMBS>;
1173            proptest!(|(value: U, shift in 0..=BITS + 2)| {
1174                let left = (value << shift).reverse_bits();
1175                let right = value.reverse_bits() >> shift;
1176                assert_eq!(left, right);
1177            });
1178        });
1179    }
1180
1181    #[test]
1182    fn test_shift_very_big_rhs() {
1183        type U = Uint<128, 2>;
1184
1185        for rhs in [
1186            U::from(u64::MAX),
1187            U::from(u128::MAX),
1188            U::from_limbs([0, 1]),
1189            U::from_limbs([1, 1]),
1190            U::from_limbs([1, u64::MAX]),
1191        ] {
1192            assert_eq!(U::ONE << rhs, U::ZERO, "{rhs}");
1193            assert_eq!(U::ONE >> rhs, U::ZERO, "{rhs}");
1194        }
1195    }
1196
1197    #[test]
1198    fn test_rotate() {
1199        const_for!(BITS in SIZES {
1200            const LIMBS: usize = nlimbs(BITS);
1201            type U = Uint::<BITS, LIMBS>;
1202            proptest!(|(value: U, shift in  0..=BITS + 2)| {
1203                let rotated = value.rotate_left(shift).rotate_right(shift);
1204                assert_eq!(value, rotated);
1205            });
1206        });
1207    }
1208
1209    #[test]
1210    fn test_arithmetic_shr() {
1211        const_for!(BITS in SIZES {
1212            const LIMBS: usize = nlimbs(BITS);
1213            type U = Uint::<BITS, LIMBS>;
1214            proptest!(|(value: U, shift in  0..=BITS + 2)| {
1215                let shifted = value.arithmetic_shr(shift);
1216                assert_eq!(shifted.leading_ones(), match value.leading_ones() {
1217                    0 => 0,
1218                    n => min(BITS, n + shift)
1219                });
1220            });
1221        });
1222    }
1223
1224    #[test]
1225    fn test_overflowing_shr() {
1226        // Test: Single limb right shift from 40u64 by 1 bit.
1227        // Expects resulting integer: 20 with no fractional part.
1228        assert_eq!(
1229            Uint::<64, 1>::from_limbs([40u64]).overflowing_shr(1),
1230            (Uint::<64, 1>::from(20), false)
1231        );
1232
1233        // Test: Single limb right shift from 41u64 by 1 bit.
1234        // Expects resulting integer: 20 with a detected fractional part.
1235        assert_eq!(
1236            Uint::<64, 1>::from_limbs([41u64]).overflowing_shr(1),
1237            (Uint::<64, 1>::from(20), true)
1238        );
1239
1240        // Test: Two limbs right shift from 0x0010_0000_0000_0000 and 0 by 1 bit.
1241        // Expects resulting limbs: [0x0080_0000_0000_000, 0] with no fractional part.
1242        assert_eq!(
1243            Uint::<65, 2>::from_limbs([0x0010_0000_0000_0000, 0]).overflowing_shr(1),
1244            (Uint::<65, 2>::from_limbs([0x0008_0000_0000_0000, 0]), false)
1245        );
1246
1247        // Test: Shift beyond single limb capacity with MAX value.
1248        // Expects the highest possible value in 256-bit representation with a detected
1249        // fractional part.
1250        assert_eq!(
1251            Uint::<256, 4>::MAX.overflowing_shr(65),
1252            (
1253                Uint::<256, 4>::from_str_radix(
1254                    "7fffffffffffffffffffffffffffffffffffffffffffffff",
1255                    16
1256                )
1257                .unwrap(),
1258                true
1259            )
1260        );
1261        // Test: Large 4096-bit integer right shift by 34 bits.
1262        // Expects a specific value with no fractional part.
1263        assert_eq!(
1264            Uint::<4096, 64>::from_str_radix("3ffffffffffffffffffffffffffffc00000000", 16,)
1265                .unwrap()
1266                .overflowing_shr(34),
1267            (
1268                Uint::<4096, 64>::from_str_radix("fffffffffffffffffffffffffffff", 16).unwrap(),
1269                false
1270            )
1271        );
1272        // Test: Extremely large 4096-bit integer right shift by 100 bits.
1273        // Expects a specific value with no fractional part.
1274        assert_eq!(
1275            Uint::<4096, 64>::from_str_radix(
1276                "fffffffffffffffffffffffffffff0000000000000000000000000",
1277                16,
1278            )
1279            .unwrap()
1280            .overflowing_shr(100),
1281            (
1282                Uint::<4096, 64>::from_str_radix("fffffffffffffffffffffffffffff", 16).unwrap(),
1283                false
1284            )
1285        );
1286        // Test: Complex 4096-bit integer right shift by 1 bit.
1287        // Expects a specific value with no fractional part.
1288        assert_eq!(
1289            Uint::<4096, 64>::from_str_radix(
1290                "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0bdbfe",
1291                16,
1292            )
1293            .unwrap()
1294            .overflowing_shr(1),
1295            (
1296                Uint::<4096, 64>::from_str_radix(
1297                    "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff85edff",
1298                    16
1299                )
1300                .unwrap(),
1301                false
1302            )
1303        );
1304        // Test: Large 4096-bit integer right shift by 1000 bits.
1305        // Expects a specific value with no fractional part.
1306        assert_eq!(
1307            Uint::<4096, 64>::from_str_radix(
1308                "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
1309                16,
1310            )
1311            .unwrap()
1312            .overflowing_shr(1000),
1313            (
1314                Uint::<4096, 64>::from_str_radix(
1315                    "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1316                    16
1317                )
1318                .unwrap(),
1319                false
1320            )
1321        );
1322        // Test: MAX 4096-bit integer right shift by 34 bits.
1323        // Expects a specific value with a detected fractional part.
1324        assert_eq!(
1325            Uint::<4096, 64>::MAX
1326            .overflowing_shr(34),
1327            (
1328                Uint::<4096, 64>::from_str_radix(
1329                    "3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1330                    16
1331                )
1332                .unwrap(),
1333                true
1334            )
1335        );
1336    }
1337
1338    #[test]
1339    fn test_strict_shl_shr_ok() {
1340        use crate::aliases::U64;
1341        assert_eq!(U64::from(1u64).strict_shl(3), U64::from(8u64));
1342        assert_eq!(U64::from(8u64).strict_shr(3), U64::from(1u64));
1343    }
1344
1345    #[test]
1346    #[should_panic(expected = "attempt to shift left with overflow")]
1347    fn test_strict_shl_overflow() {
1348        let _ = crate::aliases::U64::MAX.strict_shl(1);
1349    }
1350
1351    #[test]
1352    #[should_panic(expected = "attempt to shift right with overflow")]
1353    fn test_strict_shr_overflow() {
1354        let _ = crate::aliases::U64::from(1u64).strict_shr(1);
1355    }
1356
1357    #[test]
1358    fn regression_overflowing_shl() {
1359        // limbs entirely shifted out are caught
1360        let num = Uint::<128, 2>::from_limbs([0, 1]);
1361        assert_eq!(num.overflowing_shl(64), (Uint::ZERO, true));
1362        assert!(num.checked_shl(64).is_none());
1363
1364        // masked bits are caught
1365        let num = Uint::<65, 2>::from_limbs([0, 1]);
1366        assert_eq!(num.overflowing_shl(1), (Uint::ZERO, true));
1367        assert_eq!(num.overflowing_shl(64), (Uint::ZERO, true));
1368    }
1369
1370    #[test]
1371    fn regression_overflowing_shr() {
1372        // limbs entirely shifted out are caught
1373        let num = Uint::<128, 2>::from(1u64);
1374        assert_eq!(num.overflowing_shr(64), (Uint::ZERO, true));
1375        assert!(num.checked_shr(64).is_none());
1376    }
1377
1378    // On 32-bit targets `usize` cannot exceed `u32::MAX`, so the truncation
1379    // this guards against cannot occur (and `1usize << 32` would not compile).
1380    #[cfg(target_pointer_width = "64")]
1381    #[test]
1382    fn regression_wrapping_shifts() {
1383        // shift amounts >= BITS produce zero; the amount must not be
1384        // reduced mod 2^32 by the primitive fast paths (LIMBS = 1, 2, 4)
1385        let huge = 1usize << 32;
1386        assert_eq!(Uint::<64, 1>::from(1u64).wrapping_shl(huge), Uint::ZERO);
1387        assert_eq!(Uint::<64, 1>::from(1u64).wrapping_shl(huge + 3), Uint::ZERO);
1388        assert_eq!(Uint::<64, 1>::MAX.wrapping_shr(huge), Uint::ZERO);
1389        assert_eq!(Uint::<128, 2>::MAX.wrapping_shl(huge), Uint::ZERO);
1390        assert_eq!(Uint::<256, 4>::MAX.wrapping_shr(huge), Uint::ZERO);
1391        // truncating this amount lands back in range (130, cross-term 2), so
1392        // it exercises every internal selector of the 256-bit fast path
1393        assert_eq!(Uint::<256, 4>::MAX.wrapping_shl(huge + 130), Uint::ZERO);
1394        assert_eq!(Uint::<256, 4>::MAX.wrapping_shr(huge + 130), Uint::ZERO);
1395        // the generic path (3 limbs) already handles this
1396        assert_eq!(Uint::<192, 3>::from(1u64).wrapping_shl(huge), Uint::ZERO);
1397
1398        // the operators route through wrapping_shl/wrapping_shr
1399        assert_eq!(Uint::<64, 1>::from(1u64) << huge, Uint::ZERO);
1400        assert_eq!(Uint::<64, 1>::MAX >> huge, Uint::ZERO);
1401
1402        // arithmetic_shr: fills with the sign bit for huge shift amounts
1403        assert_eq!(Uint::<64, 1>::from(5u64).arithmetic_shr(huge), Uint::ZERO);
1404        assert_eq!(Uint::<64, 1>::MAX.arithmetic_shr(huge), Uint::<64, 1>::MAX);
1405    }
1406
1407    #[test]
1408    fn regression_overflowing_big() {
1409        // The `_big` shift helpers take a `Self` shift amount and narrowed it
1410        // to u64, then cast to usize. On 32-bit targets that cast truncated
1411        // u64 -> u32, wrapping shift amounts in [2^32, 2^64) mod 2^32 (audit
1412        // 1.9): e.g. `U256::ONE << U256::from(1u64 << 32)` shifted by 0 and
1413        // returned 1. The pointer-width-aware `usize::try_from` now shifts the
1414        // whole value out instead, so the result is correct on every pointer
1415        // width. These assertions pass on 64-bit hosts and would have failed
1416        // on wasm32.
1417        type U = Uint<256, 4>;
1418
1419        // The 1.9 repro amount: 2^32 >= BITS, so the whole value shifts out.
1420        let big = U::from(1u64 << 32);
1421        assert_eq!(U::ONE.overflowing_shl_big(big), (U::ZERO, true));
1422        assert_eq!(U::ONE.overflowing_shr_big(big), (U::ZERO, true));
1423        // the operators route through the `_big` helpers
1424        assert_eq!(U::ONE << big, U::ZERO);
1425        assert_eq!(U::MAX >> big, U::ZERO);
1426
1427        // A shift amount of exactly BITS still shifts everything out.
1428        assert_eq!(U::MAX.overflowing_shl_big(U::from(256)), (U::ZERO, true));
1429        assert_eq!(U::MAX.overflowing_shr_big(U::from(256)), (U::ZERO, true));
1430
1431        // Zero never sets the overflow flag, no matter how large the shift.
1432        assert_eq!(U::ZERO.overflowing_shl_big(big), (U::ZERO, false));
1433        assert_eq!(U::ZERO.overflowing_shr_big(big), (U::ZERO, false));
1434
1435        // Shift amounts that exceed u64 take the `try_from` branch.
1436        let over_u64 = U::from_limbs([0, 0, 1, 0]); // 2^128 > u64::MAX
1437        assert_eq!(U::MAX.overflowing_shl_big(over_u64), (U::ZERO, true));
1438        assert_eq!(U::MAX.overflowing_shr_big(over_u64), (U::ZERO, true));
1439        // ... and zero still reports no overflow there.
1440        assert_eq!(U::ZERO.overflowing_shl_big(over_u64), (U::ZERO, false));
1441        assert_eq!(U::ZERO.overflowing_shr_big(over_u64), (U::ZERO, false));
1442
1443        // In-range shifts (rhs < BITS) still return the real result.
1444        assert_eq!(
1445            U::ONE.overflowing_shl_big(U::from(8)),
1446            (U::from(256), false)
1447        );
1448        assert_eq!(
1449            U::from(256).overflowing_shr_big(U::from(8)),
1450            (U::ONE, false)
1451        );
1452    }
1453}