Skip to main content

fixed_bigint/fixeduint/
bit_ops_impl.rs

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