ruint2/
bits.rs

1use crate::Uint;
2
3use core::ops::{
4    BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, ShlAssign, Shr,
5    ShrAssign,
6};
7
8impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
9    #[must_use]
10    pub const fn bit(&self, index: usize) -> bool {
11        if index >= BITS {
12            return false;
13        }
14        let (limbs, bits) = (index / 64, index % 64);
15        self.limbs[limbs] & (1 << bits) != 0
16    }
17
18    pub fn set_bit(&mut self, index: usize, value: bool) {
19        if index >= BITS {
20            return;
21        }
22        let (limbs, bits) = (index / 64, index % 64);
23        if value {
24            self.limbs[limbs] |= 1 << bits;
25        } else {
26            self.limbs[limbs] &= !(1 << bits);
27        }
28    }
29
30    /// Reverses the order of bits in the integer. The least significant bit
31    /// becomes the most significant bit, second least-significant bit becomes
32    /// second most-significant bit, etc.
33    #[must_use]
34    pub fn reverse_bits(mut self) -> Self {
35        self.limbs.reverse();
36        for limb in &mut self.limbs {
37            *limb = limb.reverse_bits();
38        }
39        if BITS % 64 != 0 {
40            self >>= 64 - BITS % 64;
41        }
42        self
43    }
44
45    /// Returns the number of leading zeros in the binary representation of
46    /// `self`.
47    #[must_use]
48    pub fn leading_zeros(&self) -> usize {
49        self.as_limbs()
50            .iter()
51            .rev()
52            .position(|&limb| limb != 0)
53            .map_or(BITS, |n| {
54                let fixed = Self::MASK.leading_zeros() as usize;
55                let skipped = n * 64;
56                let top = self.as_limbs()[LIMBS - n - 1].leading_zeros() as usize;
57                skipped + top - fixed
58            })
59    }
60
61    /// Returns the number of leading zeros in the binary representation of
62    /// `self`.
63    #[must_use]
64    pub fn leading_ones(&self) -> usize {
65        self.as_limbs()
66            .iter()
67            .rev()
68            .position(|&limb| limb != u64::MAX)
69            .map_or(BITS, |n| {
70                let fixed = Self::MASK.leading_zeros() as usize;
71                let skipped = n * 64;
72                let top = self.as_limbs()[LIMBS - n - 1].leading_ones() as usize;
73                skipped + top - fixed
74            })
75    }
76
77    /// Returns the number of trailing zeros in the binary representation of
78    /// `self`.
79    #[must_use]
80    pub fn trailing_zeros(&self) -> usize {
81        self.as_limbs()
82            .iter()
83            .position(|&limb| limb != 0)
84            .map_or(BITS, |n| {
85                n * 64 + self.as_limbs()[n].trailing_zeros() as usize
86            })
87    }
88
89    /// Returns the number of trailing ones in the binary representation of
90    /// `self`.
91    #[must_use]
92    pub fn trailing_ones(&self) -> usize {
93        self.as_limbs()
94            .iter()
95            .position(|&limb| limb != u64::MAX)
96            .map_or(BITS, |n| {
97                n * 64 + self.as_limbs()[n].trailing_ones() as usize
98            })
99    }
100
101    /// Returns the number of ones in the binary representation of `self`.
102    #[must_use]
103    pub fn count_ones(&self) -> usize {
104        self.as_limbs()
105            .iter()
106            .map(|limb| limb.count_ones() as usize)
107            .sum()
108    }
109
110    /// Returns the number of zeros in the binary representation of `self`.
111    #[must_use]
112    pub fn count_zeros(&self) -> usize {
113        BITS - self.count_ones()
114    }
115
116    /// Length of the number in bits ignoring leading zeros.
117    #[must_use]
118    pub fn bit_len(&self) -> usize {
119        BITS - self.leading_zeros()
120    }
121
122    /// Length of the number in bytes ignoring leading zeros.
123    #[must_use]
124    pub fn byte_len(&self) -> usize {
125        (self.bit_len() + 7) / 8
126    }
127
128    /// Returns the most significant 64 bits of the number and the exponent.
129    ///
130    /// Given return value $(\mathtt{bits}, \mathtt{exponent})$, the `self` can
131    /// be approximated as
132    ///
133    /// $$
134    /// \mathtt{self} ≈ \mathtt{bits} ⋅ 2^\mathtt{exponent}
135    /// $$
136    ///
137    /// If `self` is $<≥> 2^{63}$, then `exponent` will be zero and `bits` will
138    /// have leading zeros.
139    #[must_use]
140    pub fn most_significant_bits(&self) -> (u64, usize) {
141        let first_set_limb = self
142            .as_limbs()
143            .iter()
144            .rposition(|&limb| limb != 0)
145            .unwrap_or(0);
146        if first_set_limb == 0 {
147            (self.as_limbs().first().copied().unwrap_or(0), 0)
148        } else {
149            let hi = self.as_limbs()[first_set_limb];
150            let lo = self.as_limbs()[first_set_limb - 1];
151            let leading_zeros = hi.leading_zeros();
152            let bits = if leading_zeros > 0 {
153                (hi << leading_zeros) | (lo >> (64 - leading_zeros))
154            } else {
155                hi
156            };
157            let exponent = first_set_limb * 64 - leading_zeros as usize;
158            (bits, exponent)
159        }
160    }
161
162    /// Checked left shift by `rhs` bits.
163    ///
164    /// Returns $\mathtt{self} ⋅ 2^{\mathtt{rhs}}$ or [`None`] if the result
165    /// would $≥ 2^{\mathtt{BITS}}$. That is, it returns [`None`] if the bits
166    /// shifted out would be non-zero.
167    ///
168    /// Note: This differs from [`u64::checked_shl`] which returns `None` if the
169    /// shift is larger than BITS (which is IMHO not very useful).
170    #[allow(clippy::inline_always)]
171    #[inline(always)]
172    #[must_use]
173    pub fn checked_shl(self, rhs: usize) -> Option<Self> {
174        match self.overflowing_shl(rhs) {
175            (value, false) => Some(value),
176            _ => None,
177        }
178    }
179
180    /// Saturating left shift by `rhs` bits.
181    ///
182    /// Returns $\mathtt{self} ⋅ 2^{\mathtt{rhs}}$ or [`Uint::MAX`] if the
183    /// result would $≥ 2^{\mathtt{BITS}}$. That is, it returns
184    /// [`Uint::MAX`] if the bits shifted out would be non-zero.
185    #[allow(clippy::inline_always)]
186    #[inline(always)]
187    #[must_use]
188    pub fn saturating_shl(self, rhs: usize) -> Self {
189        match self.overflowing_shl(rhs) {
190            (value, false) => value,
191            _ => Self::MAX,
192        }
193    }
194
195    #[allow(clippy::doc_markdown)]
196    /// Left shift by `rhs` bits with overflow detection.
197    ///
198    /// Returns $\mod{\mathtt{value} ⋅ 2^{\mathtt{rhs}}}_{2^{\mathtt{BITS}}}$.
199    /// If the product is $≥ 2^{\mathtt{BITS}}$ it returns `true`. That is, it
200    /// returns true if the bits shifted out are non-zero.
201    ///
202    /// Note: This differs from [`u64::overflowing_shl`] which returns `true` if
203    /// the shift is larger than `BITS` (which is IMHO not very useful).
204    #[must_use]
205    pub fn overflowing_shl(mut self, rhs: usize) -> (Self, bool) {
206        let (limbs, bits) = (rhs / 64, rhs % 64);
207        if limbs >= LIMBS {
208            return (Self::ZERO, self != Self::ZERO);
209        }
210        if bits == 0 {
211            // Check for overflow
212            let mut overflow = false;
213            for i in (LIMBS - limbs)..LIMBS {
214                overflow |= self.limbs[i] != 0;
215            }
216            if self.limbs[LIMBS - limbs - 1] > Self::MASK {
217                overflow = true;
218            }
219
220            // Shift
221            for i in (limbs..LIMBS).rev() {
222                self.limbs[i] = self.limbs[i - limbs];
223            }
224            for i in 0..limbs {
225                self.limbs[i] = 0;
226            }
227            self.limbs[LIMBS - 1] &= Self::MASK;
228            return (self, overflow);
229        }
230
231        // Check for overflow
232        let mut overflow = false;
233        for i in (LIMBS - limbs)..LIMBS {
234            overflow |= self.limbs[i] != 0;
235        }
236        if self.limbs[LIMBS - limbs - 1] >> (64 - bits) != 0 {
237            overflow = true;
238        }
239        if self.limbs[LIMBS - limbs - 1] << bits > Self::MASK {
240            overflow = true;
241        }
242
243        // Shift
244        for i in (limbs + 1..LIMBS).rev() {
245            self.limbs[i] = self.limbs[i - limbs] << bits;
246            self.limbs[i] |= self.limbs[i - limbs - 1] >> (64 - bits);
247        }
248        self.limbs[limbs] = self.limbs[0] << bits;
249        for i in 0..limbs {
250            self.limbs[i] = 0;
251        }
252        self.limbs[LIMBS - 1] &= Self::MASK;
253        (self, overflow)
254    }
255
256    #[allow(clippy::doc_markdown)]
257    /// Left shift by `rhs` bits.
258    ///
259    /// Returns $\mod{\mathtt{value} ⋅ 2^{\mathtt{rhs}}}_{2^{\mathtt{BITS}}}$.
260    ///
261    /// Note: This differs from [`u64::wrapping_shl`] which first reduces `rhs`
262    /// by `BITS` (which is IMHO not very useful).
263    #[allow(clippy::inline_always)]
264    #[inline(always)]
265    #[must_use]
266    pub fn wrapping_shl(self, rhs: usize) -> Self {
267        self.overflowing_shl(rhs).0
268    }
269
270    /// Checked right shift by `rhs` bits.
271    ///
272    /// $$
273    /// \frac{\mathtt{self}}{2^{\mathtt{rhs}}}
274    /// $$
275    ///
276    /// Returns the above or [`None`] if the division is not exact. This is the
277    /// same as
278    ///
279    /// Note: This differs from [`u64::checked_shr`] which returns `None` if the
280    /// shift is larger than BITS (which is IMHO not very useful).
281    #[allow(clippy::inline_always)]
282    #[inline(always)]
283    #[must_use]
284    pub fn checked_shr(self, rhs: usize) -> Option<Self> {
285        match self.overflowing_shr(rhs) {
286            (value, false) => Some(value),
287            _ => None,
288        }
289    }
290
291    /// Right shift by `rhs` bits with underflow detection.
292    ///
293    /// $$
294    /// \floor{\frac{\mathtt{self}}{2^{\mathtt{rhs}}}}
295    /// $$
296    ///
297    /// Returns the above and `false` if the division was exact, and `true` if
298    /// it was rounded down. This is the same as non-zero bits being shifted
299    /// out.
300    ///
301    /// Note: This differs from [`u64::overflowing_shl`] which returns `true` if
302    /// the shift is larger than `BITS` (which is IMHO not very useful).
303    #[must_use]
304    pub fn overflowing_shr(mut self, rhs: usize) -> (Self, bool) {
305        let (limbs, bits) = (rhs / 64, rhs % 64);
306        if limbs >= LIMBS {
307            return (Self::ZERO, self != Self::ZERO);
308        }
309        if bits == 0 {
310            // Check for overflow
311            let mut overflow = false;
312            for i in 0..limbs {
313                overflow |= self.limbs[i] != 0;
314            }
315
316            // Shift
317            for i in 0..(LIMBS - limbs) {
318                self.limbs[i] = self.limbs[i + limbs];
319            }
320            for i in (LIMBS - limbs)..LIMBS {
321                self.limbs[i] = 0;
322            }
323            return (self, overflow);
324        }
325
326        // Check for overflow
327        let mut overflow = false;
328        for i in 0..limbs {
329            overflow |= self.limbs[i] != 0;
330        }
331        overflow |= self.limbs[limbs] >> bits != 0;
332
333        // Shift
334        for i in 0..(LIMBS - limbs - 1) {
335            self.limbs[i] = self.limbs[i + limbs] >> bits;
336            self.limbs[i] |= self.limbs[i + limbs + 1] << (64 - bits);
337        }
338        self.limbs[LIMBS - limbs - 1] = self.limbs[LIMBS - 1] >> bits;
339        for i in (LIMBS - limbs)..LIMBS {
340            self.limbs[i] = 0;
341        }
342        (self, overflow)
343    }
344
345    /// Right shift by `rhs` bits.
346    ///
347    /// $$
348    /// \mathtt{wrapping\\_shr}(\mathtt{self}, \mathtt{rhs}) =
349    /// \floor{\frac{\mathtt{self}}{2^{\mathtt{rhs}}}}
350    /// $$
351    ///
352    /// Note: This differs from [`u64::wrapping_shr`] which first reduces `rhs`
353    /// by `BITS` (which is IMHO not very useful).
354    #[allow(clippy::inline_always)]
355    #[inline(always)]
356    #[must_use]
357    pub fn wrapping_shr(self, rhs: usize) -> Self {
358        self.overflowing_shr(rhs).0
359    }
360
361    /// Shifts the bits to the left by a specified amount, `rhs`, wrapping the
362    /// truncated bits to the end of the resulting integer.
363    #[must_use]
364    #[allow(clippy::missing_const_for_fn)] // False positive
365    pub fn rotate_left(self, rhs: usize) -> Self {
366        if BITS == 0 {
367            return Self::ZERO;
368        }
369        let rhs = rhs % BITS;
370        self << rhs | self >> (BITS - rhs)
371    }
372
373    #[allow(clippy::inline_always)]
374    #[inline(always)]
375    #[must_use]
376    pub fn rotate_right(self, rhs: usize) -> Self {
377        if BITS == 0 {
378            return Self::ZERO;
379        }
380        let rhs = rhs % BITS;
381        self.rotate_left(BITS - rhs)
382    }
383}
384
385impl<const BITS: usize, const LIMBS: usize> Not for Uint<BITS, LIMBS> {
386    type Output = Self;
387
388    fn not(mut self) -> Self::Output {
389        if BITS == 0 {
390            return Self::ZERO;
391        }
392        for limb in self.limbs.iter_mut() {
393            *limb = u64::not(*limb);
394        }
395        self.limbs[LIMBS - 1] &= Self::MASK;
396        self
397    }
398}
399
400impl<const BITS: usize, const LIMBS: usize> Not for &Uint<BITS, LIMBS> {
401    type Output = Uint<BITS, LIMBS>;
402
403    fn not(self) -> Self::Output {
404        (*self).not()
405    }
406}
407
408macro_rules! impl_bit_op {
409    ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => {
410        impl<const BITS: usize, const LIMBS: usize> $trait_assign<Uint<BITS, LIMBS>>
411            for Uint<BITS, LIMBS>
412        {
413            #[allow(clippy::inline_always)]
414            #[inline(always)]
415            fn $fn_assign(&mut self, rhs: Uint<BITS, LIMBS>) {
416                self.$fn_assign(&rhs);
417            }
418        }
419        impl<const BITS: usize, const LIMBS: usize> $trait_assign<&Uint<BITS, LIMBS>>
420            for Uint<BITS, LIMBS>
421        {
422            fn $fn_assign(&mut self, rhs: &Uint<BITS, LIMBS>) {
423                for (limb, rhs) in self.limbs.iter_mut().zip(rhs.limbs) {
424                    u64::$fn_assign(limb, rhs);
425                }
426            }
427        }
428        impl<const BITS: usize, const LIMBS: usize> $trait<Uint<BITS, LIMBS>>
429            for Uint<BITS, LIMBS>
430        {
431            type Output = Uint<BITS, LIMBS>;
432
433            #[allow(clippy::inline_always)]
434            #[inline(always)]
435            fn $fn(mut self, rhs: Uint<BITS, LIMBS>) -> Self::Output {
436                self.$fn_assign(rhs);
437                self
438            }
439        }
440        impl<const BITS: usize, const LIMBS: usize> $trait<&Uint<BITS, LIMBS>>
441            for Uint<BITS, LIMBS>
442        {
443            type Output = Uint<BITS, LIMBS>;
444
445            #[allow(clippy::inline_always)]
446            #[inline(always)]
447            fn $fn(mut self, rhs: &Uint<BITS, LIMBS>) -> Self::Output {
448                self.$fn_assign(rhs);
449                self
450            }
451        }
452        impl<const BITS: usize, const LIMBS: usize> $trait<Uint<BITS, LIMBS>>
453            for &Uint<BITS, LIMBS>
454        {
455            type Output = Uint<BITS, LIMBS>;
456
457            #[allow(clippy::inline_always)]
458            #[inline(always)]
459            fn $fn(self, mut rhs: Uint<BITS, LIMBS>) -> Self::Output {
460                rhs.$fn_assign(self);
461                rhs
462            }
463        }
464        impl<const BITS: usize, const LIMBS: usize> $trait<&Uint<BITS, LIMBS>>
465            for &Uint<BITS, LIMBS>
466        {
467            type Output = Uint<BITS, LIMBS>;
468
469            #[allow(clippy::inline_always)]
470            #[inline(always)]
471            fn $fn(self, rhs: &Uint<BITS, LIMBS>) -> Self::Output {
472                self.clone().$fn(rhs)
473            }
474        }
475    };
476}
477
478impl_bit_op!(BitOr, bitor, BitOrAssign, bitor_assign);
479impl_bit_op!(BitAnd, bitand, BitAndAssign, bitand_assign);
480impl_bit_op!(BitXor, bitxor, BitXorAssign, bitxor_assign);
481
482impl<const BITS: usize, const LIMBS: usize> ShlAssign<usize> for Uint<BITS, LIMBS> {
483    #[allow(clippy::inline_always)]
484    #[inline(always)]
485    fn shl_assign(&mut self, rhs: usize) {
486        *self = self.wrapping_shl(rhs);
487    }
488}
489
490impl<const BITS: usize, const LIMBS: usize> ShlAssign<&usize> for Uint<BITS, LIMBS> {
491    #[allow(clippy::inline_always)]
492    #[inline(always)]
493    fn shl_assign(&mut self, rhs: &usize) {
494        *self = self.wrapping_shl(*rhs);
495    }
496}
497
498impl<const BITS: usize, const LIMBS: usize> Shl<usize> for Uint<BITS, LIMBS> {
499    type Output = Self;
500
501    #[allow(clippy::inline_always)]
502    #[inline(always)]
503    fn shl(self, rhs: usize) -> Self {
504        self.wrapping_shl(rhs)
505    }
506}
507
508impl<const BITS: usize, const LIMBS: usize> Shl<usize> for &Uint<BITS, LIMBS> {
509    type Output = Uint<BITS, LIMBS>;
510
511    #[allow(clippy::inline_always)]
512    #[inline(always)]
513    fn shl(self, rhs: usize) -> Self::Output {
514        self.wrapping_shl(rhs)
515    }
516}
517
518impl<const BITS: usize, const LIMBS: usize> Shl<&usize> for Uint<BITS, LIMBS> {
519    type Output = Self;
520
521    #[allow(clippy::inline_always)]
522    #[inline(always)]
523    fn shl(self, rhs: &usize) -> Self {
524        self.wrapping_shl(*rhs)
525    }
526}
527
528impl<const BITS: usize, const LIMBS: usize> Shl<&usize> for &Uint<BITS, LIMBS> {
529    type Output = Uint<BITS, LIMBS>;
530
531    #[allow(clippy::inline_always)]
532    #[inline(always)]
533    fn shl(self, rhs: &usize) -> Self::Output {
534        self.wrapping_shl(*rhs)
535    }
536}
537
538impl<const BITS: usize, const LIMBS: usize> ShrAssign<usize> for Uint<BITS, LIMBS> {
539    #[allow(clippy::inline_always)]
540    #[inline(always)]
541    fn shr_assign(&mut self, rhs: usize) {
542        *self = self.wrapping_shr(rhs);
543    }
544}
545
546impl<const BITS: usize, const LIMBS: usize> ShrAssign<&usize> for Uint<BITS, LIMBS> {
547    #[allow(clippy::inline_always)]
548    #[inline(always)]
549    fn shr_assign(&mut self, rhs: &usize) {
550        *self = self.wrapping_shr(*rhs);
551    }
552}
553
554impl<const BITS: usize, const LIMBS: usize> Shr<usize> for Uint<BITS, LIMBS> {
555    type Output = Self;
556
557    #[allow(clippy::inline_always)]
558    #[inline(always)]
559    fn shr(self, rhs: usize) -> Self {
560        self.wrapping_shr(rhs)
561    }
562}
563
564impl<const BITS: usize, const LIMBS: usize> Shr<usize> for &Uint<BITS, LIMBS> {
565    type Output = Uint<BITS, LIMBS>;
566
567    #[allow(clippy::inline_always)]
568    #[inline(always)]
569    fn shr(self, rhs: usize) -> Self::Output {
570        self.wrapping_shr(rhs)
571    }
572}
573
574impl<const BITS: usize, const LIMBS: usize> Shr<&usize> for Uint<BITS, LIMBS> {
575    type Output = Self;
576
577    #[allow(clippy::inline_always)]
578    #[inline(always)]
579    fn shr(self, rhs: &usize) -> Self {
580        self.wrapping_shr(*rhs)
581    }
582}
583
584impl<const BITS: usize, const LIMBS: usize> Shr<&usize> for &Uint<BITS, LIMBS> {
585    type Output = Uint<BITS, LIMBS>;
586
587    #[allow(clippy::inline_always)]
588    #[inline(always)]
589    fn shr(self, rhs: &usize) -> Self::Output {
590        self.wrapping_shr(*rhs)
591    }
592}
593
594#[cfg(test)]
595mod tests {
596    use super::*;
597    use crate::{aliases::U128, const_for, nlimbs};
598    use proptest::proptest;
599
600    #[test]
601    fn test_leading_zeros() {
602        assert_eq!(Uint::<0, 0>::ZERO.leading_zeros(), 0);
603        const_for!(BITS in NON_ZERO {
604            const LIMBS: usize = nlimbs(BITS);
605            type U = Uint::<BITS, LIMBS>;
606            assert_eq!(U::ZERO.leading_zeros(), BITS);
607            assert_eq!(U::MAX.leading_zeros(), 0);
608            assert_eq!(U::from(1).leading_zeros(), BITS - 1);
609            proptest!(|(value: U)| {
610                let zeros = value.leading_zeros();
611                assert!(zeros <= BITS);
612                assert!(zeros < BITS || value == U::ZERO);
613                if zeros < BITS {
614                    let (left, overflow) = value.overflowing_shl(zeros);
615                    assert!(!overflow);
616                    assert!(left.leading_zeros() == 0 || value == U::ZERO);
617                    assert!(left.bit(BITS - 1));
618                    assert_eq!(value >> (BITS - zeros), Uint::ZERO);
619                }
620            });
621        });
622        proptest!(|(value: u128)| {
623            let uint = U128::from(value);
624            assert_eq!(uint.leading_zeros(), value.leading_zeros() as usize);
625        });
626    }
627
628    #[test]
629    fn test_most_significant_bits() {
630        const_for!(BITS in NON_ZERO {
631            const LIMBS: usize = nlimbs(BITS);
632            type U = Uint::<BITS, LIMBS>;
633            proptest!(|(value: u64)| {
634                let value = if U::LIMBS <= 1 { value & U::MASK } else { value };
635                assert_eq!(U::from(value).most_significant_bits(), (value, 0));
636            });
637        });
638        proptest!(|(mut limbs: [u64; 2])| {
639            if limbs[1] == 0 {
640                limbs[1] = 1;
641            }
642            let (bits, exponent) = U128::from_limbs(limbs).most_significant_bits();
643            assert!(bits >= 1_u64 << 63);
644            assert_eq!(exponent, 64 - limbs[1].leading_zeros() as usize);
645        });
646    }
647
648    #[test]
649    fn test_checked_shl() {
650        assert_eq!(
651            Uint::<65, 2>::from_limbs([0x0010_0000_0000_0000, 0]).checked_shl(1),
652            Some(Uint::<65, 2>::from_limbs([0x0020_0000_0000_0000, 0]))
653        );
654        assert_eq!(
655            Uint::<127, 2>::from_limbs([0x0010_0000_0000_0000, 0]).checked_shl(64),
656            Some(Uint::<127, 2>::from_limbs([0, 0x0010_0000_0000_0000]))
657        );
658    }
659
660    #[test]
661    #[allow(clippy::cast_lossless)]
662    #[allow(clippy::cast_possible_truncation)]
663    fn test_small() {
664        const_for!(BITS in [1, 2, 8, 16, 32, 63, 64] {
665            type U = Uint::<BITS, 1>;
666            proptest!(|(a: U, b: U)| {
667                assert_eq!(a | b, U::from_limbs([a.limbs[0] | b.limbs[0]]));
668                assert_eq!(a & b, U::from_limbs([a.limbs[0] & b.limbs[0]]));
669                assert_eq!(a ^ b, U::from_limbs([a.limbs[0] ^ b.limbs[0]]));
670            });
671            proptest!(|(a: U, s in 0..BITS)| {
672                assert_eq!(a << s, U::from_limbs([a.limbs[0] << s & U::MASK]));
673                assert_eq!(a >> s, U::from_limbs([a.limbs[0] >> s]));
674            });
675        });
676        proptest!(|(a: Uint::<32, 1>, s in 0_usize..=34)| {
677            assert_eq!(a.reverse_bits(), Uint::from((a.limbs[0] as u32).reverse_bits() as u64));
678            assert_eq!(a.rotate_left(s), Uint::from((a.limbs[0] as u32).rotate_left(s as u32) as u64));
679            assert_eq!(a.rotate_right(s), Uint::from((a.limbs[0] as u32).rotate_right(s as u32) as u64));
680        });
681        proptest!(|(a: Uint::<64, 1>, s in 0_usize..=66)| {
682            assert_eq!(a.reverse_bits(), Uint::from(a.limbs[0].reverse_bits()));
683            assert_eq!(a.rotate_left(s), Uint::from(a.limbs[0].rotate_left(s as u32)));
684            assert_eq!(a.rotate_right(s), Uint::from(a.limbs[0].rotate_right(s as u32)));
685        });
686    }
687
688    #[test]
689    fn test_shift_reverse() {
690        const_for!(BITS in SIZES {
691            const LIMBS: usize = nlimbs(BITS);
692            type U = Uint::<BITS, LIMBS>;
693            proptest!(|(value: U, shift in 0..=BITS + 2)| {
694                let left = (value << shift).reverse_bits();
695                let right = value.reverse_bits() >> shift;
696                assert_eq!(left, right);
697            });
698        });
699    }
700
701    #[test]
702    fn test_rotate() {
703        const_for!(BITS in SIZES {
704            const LIMBS: usize = nlimbs(BITS);
705            type U = Uint::<BITS, LIMBS>;
706            proptest!(|(value: U, shift in  0..=BITS + 2)| {
707                let rotated = value.rotate_left(shift).rotate_right(shift);
708                assert_eq!(value, rotated);
709            });
710        });
711    }
712}