Skip to main content

fixed_bigint/fixeduint/
bit_ops_impl.rs

1use super::{const_shl_impl, const_shr_impl, FixedUInt, MachineWord};
2
3use crate::const_numtraits::{
4    ConstCheckedShl, ConstCheckedShr, ConstOverflowingShl, ConstOverflowingShr,
5    ConstUnboundedShift, ConstWrappingShl, ConstWrappingShr, ConstZero,
6};
7use crate::machineword::ConstMachineWord;
8use crate::patch_num_traits::{OverflowingShl, OverflowingShr};
9
10c0nst::c0nst! {
11    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Not for FixedUInt<T, N> {
12        type Output = Self;
13        fn not(self) -> Self::Output {
14            let mut ret = <Self as ConstZero>::zero();
15            let mut i = 0;
16            while i < N {
17                ret.array[i] = !self.array[i];
18                i += 1;
19            }
20            ret
21        }
22    }
23
24    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitAnd<&FixedUInt<T, N>> for &FixedUInt<T, N> {
25        type Output = FixedUInt<T, N>;
26        fn bitand(self, other: &FixedUInt<T, N>) -> Self::Output {
27            let mut ret = <FixedUInt<T, N> as ConstZero>::zero();
28            let mut i = 0;
29            while i < N {
30                ret.array[i] = self.array[i] & other.array[i];
31                i += 1;
32            }
33            ret
34        }
35    }
36
37    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitAnd for FixedUInt<T, N> {
38        type Output = Self;
39        fn bitand(self, other: Self) -> Self::Output {
40            (&self).bitand(&other)
41        }
42    }
43
44    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitAnd<&FixedUInt<T, N>> for FixedUInt<T, N> {
45        type Output = Self;
46        fn bitand(self, other: &FixedUInt<T, N>) -> Self::Output {
47            (&self).bitand(other)
48        }
49    }
50
51    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitAnd<FixedUInt<T, N>> for &FixedUInt<T, N> {
52        type Output = FixedUInt<T, N>;
53        fn bitand(self, other: FixedUInt<T, N>) -> Self::Output {
54            self.bitand(&other)
55        }
56    }
57
58    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitAndAssign for FixedUInt<T, N> {
59        fn bitand_assign(&mut self, other: Self) {
60            let mut i = 0;
61            while i < N {
62                self.array[i] &= other.array[i];
63                i += 1;
64            }
65        }
66    }
67
68    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitOr<&FixedUInt<T, N>> for &FixedUInt<T, N> {
69        type Output = FixedUInt<T, N>;
70        fn bitor(self, other: &FixedUInt<T, N>) -> Self::Output {
71            let mut ret = <FixedUInt<T, N> as ConstZero>::zero();
72            let mut i = 0;
73            while i < N {
74                ret.array[i] = self.array[i] | other.array[i];
75                i += 1;
76            }
77            ret
78        }
79    }
80
81    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitOr for FixedUInt<T, N> {
82        type Output = Self;
83        fn bitor(self, other: Self) -> Self::Output {
84            (&self).bitor(&other)
85        }
86    }
87
88    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitOr<&FixedUInt<T, N>> for FixedUInt<T, N> {
89        type Output = Self;
90        fn bitor(self, other: &FixedUInt<T, N>) -> Self::Output {
91            (&self).bitor(other)
92        }
93    }
94
95    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitOr<FixedUInt<T, N>> for &FixedUInt<T, N> {
96        type Output = FixedUInt<T, N>;
97        fn bitor(self, other: FixedUInt<T, N>) -> Self::Output {
98            self.bitor(&other)
99        }
100    }
101
102    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitOrAssign for FixedUInt<T, N> {
103        fn bitor_assign(&mut self, other: Self) {
104            let mut i = 0;
105            while i < N {
106                self.array[i] |= other.array[i];
107                i += 1;
108            }
109        }
110    }
111
112    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitXor<&FixedUInt<T, N>> for &FixedUInt<T, N> {
113        type Output = FixedUInt<T, N>;
114        fn bitxor(self, other: &FixedUInt<T, N>) -> Self::Output {
115            let mut ret = <FixedUInt<T, N> as ConstZero>::zero();
116            let mut i = 0;
117            while i < N {
118                ret.array[i] = self.array[i] ^ other.array[i];
119                i += 1;
120            }
121            ret
122        }
123    }
124
125    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitXor for FixedUInt<T, N> {
126        type Output = Self;
127        fn bitxor(self, other: Self) -> Self::Output {
128            (&self).bitxor(&other)
129        }
130    }
131
132    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitXor<&FixedUInt<T, N>> for FixedUInt<T, N> {
133        type Output = Self;
134        fn bitxor(self, other: &FixedUInt<T, N>) -> Self::Output {
135            (&self).bitxor(other)
136        }
137    }
138
139    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitXor<FixedUInt<T, N>> for &FixedUInt<T, N> {
140        type Output = FixedUInt<T, N>;
141        fn bitxor(self, other: FixedUInt<T, N>) -> Self::Output {
142            self.bitxor(&other)
143        }
144    }
145
146    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::BitXorAssign for FixedUInt<T, N> {
147        fn bitxor_assign(&mut self, other: Self) {
148            let mut i = 0;
149            while i < N {
150                self.array[i] ^= other.array[i];
151                i += 1;
152            }
153        }
154    }
155
156    // Primary Shl/Shr implementations
157    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shl<usize> for FixedUInt<T, N> {
158        type Output = Self;
159        fn shl(self, bits: usize) -> Self::Output {
160            let mut result = self;
161            const_shl_impl(&mut result, bits);
162            result
163        }
164    }
165
166    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shr<usize> for FixedUInt<T, N> {
167        type Output = Self;
168        fn shr(self, bits: usize) -> Self::Output {
169            let mut result = self;
170            const_shr_impl(&mut result, bits);
171            result
172        }
173    }
174
175    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shl<u32> for FixedUInt<T, N> {
176        type Output = Self;
177        fn shl(self, bits: u32) -> Self::Output {
178            self.shl(bits as usize)
179        }
180    }
181
182    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shr<u32> for FixedUInt<T, N> {
183        type Output = Self;
184        fn shr(self, bits: u32) -> Self::Output {
185            self.shr(bits as usize)
186        }
187    }
188
189    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shl<&usize> for FixedUInt<T, N> {
190        type Output = Self;
191        fn shl(self, bits: &usize) -> Self::Output {
192            self.shl(*bits)
193        }
194    }
195
196    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shr<&usize> for FixedUInt<T, N> {
197        type Output = Self;
198        fn shr(self, bits: &usize) -> Self::Output {
199            self.shr(*bits)
200        }
201    }
202
203    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shl<&u32> for FixedUInt<T, N> {
204        type Output = Self;
205        fn shl(self, bits: &u32) -> Self::Output {
206            self.shl(*bits as usize)
207        }
208    }
209
210    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shr<&u32> for FixedUInt<T, N> {
211        type Output = Self;
212        fn shr(self, bits: &u32) -> Self::Output {
213            self.shr(*bits as usize)
214        }
215    }
216
217    // Shl/Shr for &FixedUInt
218    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shl<usize> for &FixedUInt<T, N> {
219        type Output = FixedUInt<T, N>;
220        fn shl(self, bits: usize) -> Self::Output {
221            (*self).shl(bits)
222        }
223    }
224
225    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shr<usize> for &FixedUInt<T, N> {
226        type Output = FixedUInt<T, N>;
227        fn shr(self, bits: usize) -> Self::Output {
228            (*self).shr(bits)
229        }
230    }
231
232    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shl<u32> for &FixedUInt<T, N> {
233        type Output = FixedUInt<T, N>;
234        fn shl(self, bits: u32) -> Self::Output {
235            (*self).shl(bits as usize)
236        }
237    }
238
239    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shr<u32> for &FixedUInt<T, N> {
240        type Output = FixedUInt<T, N>;
241        fn shr(self, bits: u32) -> Self::Output {
242            (*self).shr(bits as usize)
243        }
244    }
245
246    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shl<&usize> for &FixedUInt<T, N> {
247        type Output = FixedUInt<T, N>;
248        fn shl(self, bits: &usize) -> Self::Output {
249            (*self).shl(*bits)
250        }
251    }
252
253    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shr<&usize> for &FixedUInt<T, N> {
254        type Output = FixedUInt<T, N>;
255        fn shr(self, bits: &usize) -> Self::Output {
256            (*self).shr(*bits)
257        }
258    }
259
260    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shl<&u32> for &FixedUInt<T, N> {
261        type Output = FixedUInt<T, N>;
262        fn shl(self, bits: &u32) -> Self::Output {
263            (*self).shl(*bits as usize)
264        }
265    }
266
267    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::Shr<&u32> for &FixedUInt<T, N> {
268        type Output = FixedUInt<T, N>;
269        fn shr(self, bits: &u32) -> Self::Output {
270            (*self).shr(*bits as usize)
271        }
272    }
273
274    // ShlAssign/ShrAssign
275    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::ShlAssign<usize> for FixedUInt<T, N> {
276        fn shl_assign(&mut self, bits: usize) {
277            const_shl_impl(self, bits);
278        }
279    }
280
281    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::ShrAssign<usize> for FixedUInt<T, N> {
282        fn shr_assign(&mut self, bits: usize) {
283            const_shr_impl(self, bits);
284        }
285    }
286
287    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::ShlAssign<&usize> for FixedUInt<T, N> {
288        fn shl_assign(&mut self, bits: &usize) {
289            const_shl_impl(self, *bits);
290        }
291    }
292
293    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::ops::ShrAssign<&usize> for FixedUInt<T, N> {
294        fn shr_assign(&mut self, bits: &usize) {
295            const_shr_impl(self, *bits);
296        }
297    }
298
299    // Helper to normalize shift amount and detect overflow.
300    // Handles both 16-bit (usize < u32) and 64-bit (bit_size > u32::MAX) platforms.
301    c0nst fn normalize_shift_amount(bits: u32, bit_size: usize) -> (usize, bool) {
302        let bit_size_u32 = bit_size as u32;
303        if bit_size == 0 {
304            // Zero-size type: always overflow
305            (0, true)
306        } else if bit_size_u32 == 0 {
307            // bit_size is a non-zero multiple of 2^32 (huge type on 64-bit).
308            // Since bits is u32, it's always smaller than bit_size. No overflow.
309            (bits as usize, false)
310        } else if bits >= bit_size_u32 {
311            // Normal case: shift exceeds bit width
312            ((bits % bit_size_u32) as usize, true)
313        } else {
314            (bits as usize, false)
315        }
316    }
317
318    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstOverflowingShl for FixedUInt<T, N> {
319        fn overflowing_shl(&self, bits: u32) -> (Self, bool) {
320            let (shift, overflow) = normalize_shift_amount(bits, Self::BIT_SIZE);
321            let res = core::ops::Shl::<usize>::shl(*self, shift);
322            (res, overflow)
323        }
324    }
325
326    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstOverflowingShr for FixedUInt<T, N> {
327        fn overflowing_shr(&self, bits: u32) -> (Self, bool) {
328            let (shift, overflow) = normalize_shift_amount(bits, Self::BIT_SIZE);
329            let res = core::ops::Shr::<usize>::shr(*self, shift);
330            (res, overflow)
331        }
332    }
333
334    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstWrappingShl for FixedUInt<T, N> {
335        fn wrapping_shl(&self, bits: u32) -> Self {
336            ConstOverflowingShl::overflowing_shl(self, bits).0
337        }
338    }
339
340    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstWrappingShr for FixedUInt<T, N> {
341        fn wrapping_shr(&self, bits: u32) -> Self {
342            ConstOverflowingShr::overflowing_shr(self, bits).0
343        }
344    }
345
346    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstCheckedShl for FixedUInt<T, N> {
347        fn checked_shl(&self, bits: u32) -> Option<Self> {
348            let (res, overflow) = ConstOverflowingShl::overflowing_shl(self, bits);
349            if overflow { None } else { Some(res) }
350        }
351    }
352
353    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstCheckedShr for FixedUInt<T, N> {
354        fn checked_shr(&self, bits: u32) -> Option<Self> {
355            let (res, overflow) = ConstOverflowingShr::overflowing_shr(self, bits);
356            if overflow { None } else { Some(res) }
357        }
358    }
359
360    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst ConstUnboundedShift for FixedUInt<T, N> {
361        fn unbounded_shl(self, rhs: u32) -> Self {
362            let (shift, overflow) = normalize_shift_amount(rhs, Self::BIT_SIZE);
363            if overflow {
364                Self::zero()
365            } else {
366                self << shift
367            }
368        }
369
370        fn unbounded_shr(self, rhs: u32) -> Self {
371            let (shift, overflow) = normalize_shift_amount(rhs, Self::BIT_SIZE);
372            if overflow {
373                Self::zero()
374            } else {
375                self >> shift
376            }
377        }
378    }
379}
380
381// OverflowingShl/Shr from patch_num_traits - delegate to const impls
382impl<T: MachineWord, const N: usize> OverflowingShl for FixedUInt<T, N> {
383    fn overflowing_shl(self, bits: u32) -> (Self, bool) {
384        ConstOverflowingShl::overflowing_shl(&self, bits)
385    }
386}
387
388impl<T: MachineWord, const N: usize> OverflowingShr for FixedUInt<T, N> {
389    fn overflowing_shr(self, bits: u32) -> (Self, bool) {
390        ConstOverflowingShr::overflowing_shr(&self, bits)
391    }
392}
393
394// num_traits wrappers - delegate to const impls
395impl<T: MachineWord, const N: usize> num_traits::WrappingShl for FixedUInt<T, N> {
396    fn wrapping_shl(&self, bits: u32) -> Self {
397        ConstWrappingShl::wrapping_shl(self, bits)
398    }
399}
400
401impl<T: MachineWord, const N: usize> num_traits::WrappingShr for FixedUInt<T, N> {
402    fn wrapping_shr(&self, bits: u32) -> Self {
403        ConstWrappingShr::wrapping_shr(self, bits)
404    }
405}
406
407impl<T: MachineWord, const N: usize> num_traits::CheckedShl for FixedUInt<T, N> {
408    fn checked_shl(&self, bits: u32) -> Option<Self> {
409        ConstCheckedShl::checked_shl(self, bits)
410    }
411}
412
413impl<T: MachineWord, const N: usize> num_traits::CheckedShr for FixedUInt<T, N> {
414    fn checked_shr(&self, bits: u32) -> Option<Self> {
415        ConstCheckedShr::checked_shr(self, bits)
416    }
417}
418
419#[cfg(test)]
420mod tests {
421    use super::*;
422
423    #[test]
424    fn test_bitand_combinations() {
425        let a = FixedUInt::<u8, 2>::from(12u8); // 1100
426        let b = FixedUInt::<u8, 2>::from(10u8); // 1010
427        let expected = FixedUInt::<u8, 2>::from(8u8); // 1000
428
429        // value & value
430        assert_eq!(a & b, expected);
431        // value & ref
432        assert_eq!(a & &b, expected);
433        // ref & value
434        assert_eq!(&a & b, expected);
435        // ref & ref
436        assert_eq!(&a & &b, expected);
437    }
438
439    #[test]
440    fn test_bitor_combinations() {
441        let a = FixedUInt::<u8, 2>::from(12u8); // 1100
442        let b = FixedUInt::<u8, 2>::from(10u8); // 1010
443        let expected = FixedUInt::<u8, 2>::from(14u8); // 1110
444
445        // value | value
446        assert_eq!(a | b, expected);
447        // value | ref
448        assert_eq!(a | &b, expected);
449        // ref | value
450        assert_eq!(&a | b, expected);
451        // ref | ref
452        assert_eq!(&a | &b, expected);
453    }
454
455    #[test]
456    fn test_bitxor_combinations() {
457        let a = FixedUInt::<u8, 2>::from(12u8); // 1100
458        let b = FixedUInt::<u8, 2>::from(10u8); // 1010
459        let expected = FixedUInt::<u8, 2>::from(6u8); // 0110
460
461        // value ^ value
462        assert_eq!(a ^ b, expected);
463        // value ^ ref
464        assert_eq!(a ^ &b, expected);
465        // ref ^ value
466        assert_eq!(&a ^ b, expected);
467        // ref ^ ref
468        assert_eq!(&a ^ &b, expected);
469    }
470
471    #[test]
472    fn test_shl_combinations() {
473        let a = FixedUInt::<u8, 2>::from(2u8); // 0010
474        let shift: usize = 2;
475        let expected = FixedUInt::<u8, 2>::from(8u8); // 1000
476
477        // value << value
478        assert_eq!(a << shift, expected);
479        // value << ref
480        assert_eq!(a << &shift, expected);
481        // ref << value
482        assert_eq!(&a << shift, expected);
483        // ref << ref
484        assert_eq!(&a << &shift, expected);
485
486        // Same with u32
487        let shift32: u32 = 2;
488        assert_eq!(a << shift32, expected);
489        assert_eq!(a << &shift32, expected);
490        assert_eq!(&a << shift32, expected);
491        assert_eq!(&a << &shift32, expected);
492    }
493
494    #[test]
495    fn test_shr_combinations() {
496        let a = FixedUInt::<u8, 2>::from(8u8); // 1000
497        let shift: usize = 2;
498        let expected = FixedUInt::<u8, 2>::from(2u8); // 0010
499
500        // value >> value
501        assert_eq!(a >> shift, expected);
502        // value >> ref
503        assert_eq!(a >> &shift, expected);
504        // ref >> value
505        assert_eq!(&a >> shift, expected);
506        // ref >> ref
507        assert_eq!(&a >> &shift, expected);
508
509        // Same with u32
510        let shift32: u32 = 2;
511        assert_eq!(a >> shift32, expected);
512        assert_eq!(a >> &shift32, expected);
513        assert_eq!(&a >> shift32, expected);
514        assert_eq!(&a >> &shift32, expected);
515    }
516
517    #[test]
518    fn test_const_bitops() {
519        type TestInt = FixedUInt<u8, 2>;
520
521        let a = TestInt::from(0b11001100u8);
522        let b = TestInt::from(0b10101010u8);
523
524        // Test not
525        let not_a = !a;
526        assert_eq!(not_a.array[0], 0b00110011);
527        assert_eq!(not_a.array[1], 0xFF);
528
529        // Test bitand
530        assert_eq!(a & b, TestInt::from(0b10001000u8));
531
532        // Test bitor
533        assert_eq!(a | b, TestInt::from(0b11101110u8));
534
535        // Test bitxor
536        assert_eq!(a ^ b, TestInt::from(0b01100110u8));
537
538        // Test shl
539        assert_eq!(TestInt::from(1u8) << 4usize, TestInt::from(16u8));
540
541        // Test shr
542        assert_eq!(TestInt::from(16u8) >> 2usize, TestInt::from(4u8));
543
544        #[cfg(feature = "nightly")]
545        {
546            const A: TestInt = FixedUInt {
547                array: [0b11001100, 0],
548            };
549            const B: TestInt = FixedUInt {
550                array: [0b10101010, 0],
551            };
552
553            const NOT_A: TestInt = !A;
554            const AND_AB: TestInt = A & B;
555            const OR_AB: TestInt = A | B;
556            const XOR_AB: TestInt = A ^ B;
557            const SHL_1: TestInt = FixedUInt { array: [1u8, 0] } << 4usize;
558            const SHR_16: TestInt = FixedUInt { array: [16u8, 0] } >> 2usize;
559
560            assert_eq!(NOT_A.array[0], 0b00110011);
561            assert_eq!(AND_AB.array[0], 0b10001000);
562            assert_eq!(OR_AB.array[0], 0b11101110);
563            assert_eq!(XOR_AB.array[0], 0b01100110);
564            assert_eq!(SHL_1.array[0], 16);
565            assert_eq!(SHR_16.array[0], 4);
566        }
567    }
568
569    #[test]
570    fn test_const_shift_traits() {
571        type TestInt = FixedUInt<u8, 2>; // 16-bit
572
573        // Test overflowing_shl
574        let a = TestInt::from(0x80u8); // 0x0080
575        let (res, overflow) = ConstOverflowingShl::overflowing_shl(&a, 8);
576        assert_eq!(res.array, [0, 0x80]); // 0x8000
577        assert!(!overflow);
578
579        let (res, overflow) = ConstOverflowingShl::overflowing_shl(&a, 16);
580        assert_eq!(res.array, [0x80, 0]); // wraps around
581        assert!(overflow);
582
583        let (res, overflow) = ConstOverflowingShl::overflowing_shl(&a, 9);
584        assert_eq!(res.array, [0, 0]); // high bits shifted out (but shift < bit_width)
585        assert!(!overflow); // 9 < 16, so no overflow
586
587        // Test overflowing_shr
588        let b = TestInt::from(0x0100u16); // 0x0100
589        let (res, overflow) = ConstOverflowingShr::overflowing_shr(&b, 8);
590        assert_eq!(res.array, [1, 0]); // 0x0001
591        assert!(!overflow);
592
593        let (res, overflow) = ConstOverflowingShr::overflowing_shr(&b, 16);
594        assert_eq!(res.array, [0, 1]); // wraps
595        assert!(overflow);
596
597        // Test wrapping_shl
598        let c = TestInt::from(1u8);
599        assert_eq!(ConstWrappingShl::wrapping_shl(&c, 4).array, [16, 0]);
600        assert_eq!(ConstWrappingShl::wrapping_shl(&c, 16).array, [1, 0]); // wraps
601        assert_eq!(ConstWrappingShl::wrapping_shl(&c, 17).array, [2, 0]); // wraps
602
603        // Test wrapping_shr
604        let d = TestInt::from(0x8000u16);
605        assert_eq!(ConstWrappingShr::wrapping_shr(&d, 4).array, [0, 0x08]);
606        assert_eq!(ConstWrappingShr::wrapping_shr(&d, 16).array, [0, 0x80]); // wraps
607        assert_eq!(ConstWrappingShr::wrapping_shr(&d, 17).array, [0, 0x40]); // wraps
608
609        // Test checked_shl
610        let e = TestInt::from(1u8);
611        assert_eq!(
612            ConstCheckedShl::checked_shl(&e, 4),
613            Some(TestInt::from(16u8))
614        );
615        assert_eq!(
616            ConstCheckedShl::checked_shl(&e, 15),
617            Some(TestInt::from(0x8000u16))
618        );
619        assert_eq!(ConstCheckedShl::checked_shl(&e, 16), None); // overflow
620
621        // Test checked_shr
622        let f = TestInt::from(0x8000u16);
623        assert_eq!(
624            ConstCheckedShr::checked_shr(&f, 15),
625            Some(TestInt::from(1u8))
626        );
627        assert_eq!(ConstCheckedShr::checked_shr(&f, 16), None); // overflow
628
629        // Test edge case: zero shift
630        let g = TestInt::from(42u8);
631        assert_eq!(ConstOverflowingShl::overflowing_shl(&g, 0), (g, false));
632        assert_eq!(ConstOverflowingShr::overflowing_shr(&g, 0), (g, false));
633        assert_eq!(ConstWrappingShl::wrapping_shl(&g, 0), g);
634        assert_eq!(ConstWrappingShr::wrapping_shr(&g, 0), g);
635        assert_eq!(ConstCheckedShl::checked_shl(&g, 0), Some(g));
636        assert_eq!(ConstCheckedShr::checked_shr(&g, 0), Some(g));
637    }
638
639    #[test]
640    fn test_const_shift_traits_n0() {
641        // Test with N=0 (zero-sized type)
642        type ZeroInt = FixedUInt<u8, 0>;
643        let z = ZeroInt { array: [] };
644
645        // All shifts on zero-sized type should overflow
646        assert_eq!(ConstOverflowingShl::overflowing_shl(&z, 0), (z, true));
647        assert_eq!(ConstOverflowingShr::overflowing_shr(&z, 0), (z, true));
648        assert_eq!(ConstWrappingShl::wrapping_shl(&z, 0), z);
649        assert_eq!(ConstWrappingShr::wrapping_shr(&z, 0), z);
650        assert_eq!(ConstCheckedShl::checked_shl(&z, 0), None);
651        assert_eq!(ConstCheckedShr::checked_shr(&z, 0), None);
652    }
653
654    #[test]
655    fn test_num_traits_shift_wrappers() {
656        use num_traits::{CheckedShl, CheckedShr, WrappingShl, WrappingShr};
657
658        type TestInt = FixedUInt<u8, 2>;
659
660        let a = TestInt::from(1u8);
661
662        // num_traits::WrappingShl delegates to ConstWrappingShl
663        assert_eq!(WrappingShl::wrapping_shl(&a, 4), TestInt::from(16u8));
664        assert_eq!(WrappingShl::wrapping_shl(&a, 16), a); // wraps
665
666        // num_traits::WrappingShr
667        let b = TestInt::from(16u8);
668        assert_eq!(WrappingShr::wrapping_shr(&b, 4), TestInt::from(1u8));
669
670        // num_traits::CheckedShl
671        assert_eq!(CheckedShl::checked_shl(&a, 4), Some(TestInt::from(16u8)));
672        assert_eq!(CheckedShl::checked_shl(&a, 16), None);
673
674        // num_traits::CheckedShr
675        assert_eq!(CheckedShr::checked_shr(&b, 4), Some(TestInt::from(1u8)));
676        assert_eq!(CheckedShr::checked_shr(&b, 16), None);
677    }
678
679    #[test]
680    fn test_unbounded_shift() {
681        type U16 = FixedUInt<u8, 2>;
682
683        let one = U16::from(1u8);
684
685        // Normal shifts (within bounds)
686        assert_eq!(ConstUnboundedShift::unbounded_shl(one, 0), one);
687        assert_eq!(ConstUnboundedShift::unbounded_shl(one, 4), U16::from(16u8));
688        assert_eq!(
689            ConstUnboundedShift::unbounded_shl(one, 15),
690            U16::from(0x8000u16)
691        );
692
693        assert_eq!(
694            ConstUnboundedShift::unbounded_shr(U16::from(0x8000u16), 15),
695            one
696        );
697        assert_eq!(ConstUnboundedShift::unbounded_shr(U16::from(16u8), 4), one);
698
699        // At boundary (shift by bit width) - returns 0
700        assert_eq!(ConstUnboundedShift::unbounded_shl(one, 16), U16::from(0u8));
701        assert_eq!(
702            ConstUnboundedShift::unbounded_shr(U16::from(0xFFFFu16), 16),
703            U16::from(0u8)
704        );
705
706        // Beyond boundary - returns 0
707        assert_eq!(
708            ConstUnboundedShift::unbounded_shl(U16::from(0xFFFFu16), 17),
709            U16::from(0u8)
710        );
711        assert_eq!(
712            ConstUnboundedShift::unbounded_shl(U16::from(0xFFFFu16), 100),
713            U16::from(0u8)
714        );
715        assert_eq!(
716            ConstUnboundedShift::unbounded_shr(U16::from(0xFFFFu16), 17),
717            U16::from(0u8)
718        );
719        assert_eq!(
720            ConstUnboundedShift::unbounded_shr(U16::from(0xFFFFu16), 100),
721            U16::from(0u8)
722        );
723
724        // Test with different word sizes
725        type U32 = FixedUInt<u8, 4>;
726        let one32 = U32::from(1u8);
727        assert_eq!(
728            ConstUnboundedShift::unbounded_shl(one32, 31),
729            U32::from(0x80000000u32)
730        );
731        assert_eq!(
732            ConstUnboundedShift::unbounded_shl(one32, 32),
733            U32::from(0u8)
734        );
735        assert_eq!(
736            ConstUnboundedShift::unbounded_shr(U32::from(0x80000000u32), 31),
737            one32
738        );
739        assert_eq!(
740            ConstUnboundedShift::unbounded_shr(U32::from(0x80000000u32), 32),
741            U32::from(0u8)
742        );
743    }
744
745    #[test]
746    fn test_unbounded_shift_polymorphic() {
747        fn test_unbounded<T>(val: T, shift: u32, expected_shl: T, expected_shr: T)
748        where
749            T: ConstUnboundedShift + Eq + core::fmt::Debug + Copy,
750        {
751            assert_eq!(ConstUnboundedShift::unbounded_shl(val, shift), expected_shl);
752            assert_eq!(ConstUnboundedShift::unbounded_shr(val, shift), expected_shr);
753        }
754
755        // Test with FixedUInt layouts
756        type U8x2 = FixedUInt<u8, 2>;
757        type U8x4 = FixedUInt<u8, 4>;
758        type U16x2 = FixedUInt<u16, 2>;
759
760        // Same logical shift, different layouts
761        test_unbounded(U8x2::from(1u8), 4, U8x2::from(16u8), U8x2::from(0u8));
762        test_unbounded(U8x4::from(1u8), 4, U8x4::from(16u8), U8x4::from(0u8));
763        test_unbounded(U16x2::from(1u8), 4, U16x2::from(16u8), U16x2::from(0u8));
764
765        // Test with primitives
766        test_unbounded(1u8, 4, 16u8, 0u8);
767        test_unbounded(1u16, 4, 16u16, 0u16);
768        test_unbounded(1u32, 4, 16u32, 0u32);
769
770        // Boundary tests
771        test_unbounded(1u8, 8, 0u8, 0u8);
772        test_unbounded(U8x2::from(1u8), 16, U8x2::from(0u8), U8x2::from(0u8));
773    }
774}