Skip to main content

decimal_scaled/
bitwise.rs

1//! Bitwise operations on `I128`'s underlying `i128` storage.
2//!
3//! Every operator and method here delegates directly to the equivalent
4//! `i128` intrinsic on the raw storage field. They operate on the
5//! **raw storage bits**, not the logical decimal value.
6//!
7//! # Storage-not-value semantic
8//!
9//! `I128<SCALE>` stores its value as `raw * 10^(-SCALE)`, so a logical
10//! value of `1.0` at `SCALE = 12` has raw storage `10^12`, not `1`.
11//! Bitwise operations see that raw integer, not the logical decimal.
12//!
13//! ```ignore
14//! use decimal_scaled::I128s12;
15//! // I128s12::ONE.to_bits() == 1_000_000_000_000 (= 10^12), NOT 1.
16//! // count_ones() returns the popcount of 10^12, which is 21.
17//! assert_eq!(I128s12::ONE.count_ones(), 21);
18//! ```
19//!
20//! For predictable bit-pattern test data, construct values with
21//! [`I128::from_bits`], which sets the raw `i128` directly.
22//!
23//! # Operator semantics
24//!
25//! - `Shr` is **arithmetic** (sign-extending), matching `i128`'s default.
26//!   Negative values remain negative after a right shift.
27//! - [`I128::unsigned_shr`] is the **logical** (zero-fill) right shift:
28//!   the storage is reinterpreted as `u128`, shifted, then cast back.
29//! - `Not` (`!self`) flips every bit of the underlying `i128`.
30//! - `Shl` and `Shr` panic in debug builds when the shift amount is >= 128
31//!   (standard Rust integer-shift overflow contract).
32//!
33//! # `no_std` compatibility
34//!
35//! All items in this module are pure `i128` or `u128` operations and
36//! require neither `std` nor `alloc`. They compile under
37//! `--no-default-features`.
38
39use core::ops::{
40    BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, ShlAssign, Shr,
41    ShrAssign,
42};
43
44use crate::core_type::I128;
45
46// -- BitAnd -----------------------------------------------------------
47
48impl<const SCALE: u32> BitAnd for I128<SCALE> {
49    type Output = Self;
50
51    /// Bitwise AND of the underlying `i128` storage values.
52    ///
53    /// Operates on raw bits, not the logical decimal value.
54    ///
55    /// # Precision
56    ///
57    /// Strict: all arithmetic is integer-only; result is bit-exact.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// use decimal_scaled::I128s12;
63    /// let a = I128s12::from_bits(0b1100);
64    /// let b = I128s12::from_bits(0b1010);
65    /// assert_eq!(a & b, I128s12::from_bits(0b1000));
66    /// ```
67    #[inline]
68    fn bitand(self, rhs: Self) -> Self {
69        Self(self.0 & rhs.0)
70    }
71}
72
73impl<const SCALE: u32> BitAndAssign for I128<SCALE> {
74    /// In-place bitwise AND of the underlying `i128` storage.
75    ///
76    /// Operates on raw bits, not the logical decimal value.
77    ///
78    /// # Precision
79    ///
80    /// Strict: all arithmetic is integer-only; result is bit-exact.
81    #[inline]
82    fn bitand_assign(&mut self, rhs: Self) {
83        self.0 &= rhs.0;
84    }
85}
86
87// -- BitOr ------------------------------------------------------------
88
89impl<const SCALE: u32> BitOr for I128<SCALE> {
90    type Output = Self;
91
92    /// Bitwise OR of the underlying `i128` storage values.
93    ///
94    /// Operates on raw bits, not the logical decimal value.
95    ///
96    /// # Precision
97    ///
98    /// Strict: all arithmetic is integer-only; result is bit-exact.
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use decimal_scaled::I128s12;
104    /// let a = I128s12::from_bits(0b1100);
105    /// let b = I128s12::from_bits(0b1010);
106    /// assert_eq!(a | b, I128s12::from_bits(0b1110));
107    /// ```
108    #[inline]
109    fn bitor(self, rhs: Self) -> Self {
110        Self(self.0 | rhs.0)
111    }
112}
113
114impl<const SCALE: u32> BitOrAssign for I128<SCALE> {
115    /// In-place bitwise OR of the underlying `i128` storage.
116    ///
117    /// Operates on raw bits, not the logical decimal value.
118    ///
119    /// # Precision
120    ///
121    /// Strict: all arithmetic is integer-only; result is bit-exact.
122    #[inline]
123    fn bitor_assign(&mut self, rhs: Self) {
124        self.0 |= rhs.0;
125    }
126}
127
128// -- BitXor -----------------------------------------------------------
129
130impl<const SCALE: u32> BitXor for I128<SCALE> {
131    type Output = Self;
132
133    /// Bitwise XOR of the underlying `i128` storage values.
134    ///
135    /// Operates on raw bits, not the logical decimal value.
136    ///
137    /// # Precision
138    ///
139    /// Strict: all arithmetic is integer-only; result is bit-exact.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// use decimal_scaled::I128s12;
145    /// let a = I128s12::from_bits(0b1100);
146    /// let b = I128s12::from_bits(0b1010);
147    /// assert_eq!(a ^ b, I128s12::from_bits(0b0110));
148    /// ```
149    #[inline]
150    fn bitxor(self, rhs: Self) -> Self {
151        Self(self.0 ^ rhs.0)
152    }
153}
154
155impl<const SCALE: u32> BitXorAssign for I128<SCALE> {
156    /// In-place bitwise XOR of the underlying `i128` storage.
157    ///
158    /// Operates on raw bits, not the logical decimal value.
159    ///
160    /// # Precision
161    ///
162    /// Strict: all arithmetic is integer-only; result is bit-exact.
163    #[inline]
164    fn bitxor_assign(&mut self, rhs: Self) {
165        self.0 ^= rhs.0;
166    }
167}
168
169// -- Shl --------------------------------------------------------------
170
171impl<const SCALE: u32> Shl<u32> for I128<SCALE> {
172    type Output = Self;
173
174    /// Left-shift the underlying `i128` storage by `n` bits.
175    ///
176    /// Operates on raw bits, not the logical decimal value.
177    /// Delegates to `i128 << n`.
178    ///
179    /// # Precision
180    ///
181    /// Strict: all arithmetic is integer-only; result is bit-exact.
182    ///
183    /// # Panics
184    ///
185    /// Panics in debug builds when `n >= 128`; wraps modulo 128 in
186    /// release builds (standard Rust integer-shift contract).
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use decimal_scaled::I128s12;
192    /// assert_eq!(I128s12::from_bits(1) << 3u32, I128s12::from_bits(8));
193    /// ```
194    #[inline]
195    fn shl(self, n: u32) -> Self {
196        Self(self.0 << n)
197    }
198}
199
200impl<const SCALE: u32> ShlAssign<u32> for I128<SCALE> {
201    /// In-place left-shift of the underlying `i128` storage by `n` bits.
202    ///
203    /// Operates on raw bits, not the logical decimal value.
204    ///
205    /// # Precision
206    ///
207    /// Strict: all arithmetic is integer-only; result is bit-exact.
208    #[inline]
209    fn shl_assign(&mut self, n: u32) {
210        self.0 <<= n;
211    }
212}
213
214// -- Shr --------------------------------------------------------------
215
216impl<const SCALE: u32> Shr<u32> for I128<SCALE> {
217    type Output = Self;
218
219    /// Arithmetic (sign-extending) right-shift of the underlying `i128`
220    /// storage by `n` bits.
221    ///
222    /// Negative values remain negative: the vacated high bits are filled
223    /// with the sign bit (ones for negative, zero for non-negative).
224    /// Use [`I128::unsigned_shr`] for a logical (zero-fill) right shift.
225    ///
226    /// Operates on raw bits, not the logical decimal value.
227    /// Delegates to `i128 >> n`.
228    ///
229    /// # Precision
230    ///
231    /// Strict: all arithmetic is integer-only; result is bit-exact.
232    ///
233    /// # Panics
234    ///
235    /// Panics in debug builds when `n >= 128`; wraps modulo 128 in
236    /// release builds.
237    ///
238    /// # Examples
239    ///
240    /// ```
241    /// use decimal_scaled::I128s12;
242    /// // Arithmetic shift: -8 >> 1 == -4.
243    /// assert_eq!(I128s12::from_bits(-8) >> 1u32, I128s12::from_bits(-4));
244    /// ```
245    #[inline]
246    fn shr(self, n: u32) -> Self {
247        Self(self.0 >> n)
248    }
249}
250
251impl<const SCALE: u32> ShrAssign<u32> for I128<SCALE> {
252    /// In-place arithmetic (sign-extending) right-shift of the underlying
253    /// `i128` storage by `n` bits.
254    ///
255    /// Operates on raw bits, not the logical decimal value.
256    ///
257    /// # Precision
258    ///
259    /// Strict: all arithmetic is integer-only; result is bit-exact.
260    #[inline]
261    fn shr_assign(&mut self, n: u32) {
262        self.0 >>= n;
263    }
264}
265
266// -- Not --------------------------------------------------------------
267
268impl<const SCALE: u32> Not for I128<SCALE> {
269    type Output = Self;
270
271    /// Bitwise complement of the underlying `i128` storage (flip every
272    /// bit).
273    ///
274    /// `!I128::ZERO` produces `I128::from_bits(-1)` (all-ones in two's
275    /// complement).
276    ///
277    /// Operates on raw bits, not the logical decimal value.
278    ///
279    /// # Precision
280    ///
281    /// Strict: all arithmetic is integer-only; result is bit-exact.
282    ///
283    /// # Examples
284    ///
285    /// ```
286    /// use decimal_scaled::I128s12;
287    /// assert_eq!(!I128s12::ZERO, I128s12::from_bits(-1));
288    /// assert_eq!(!I128s12::from_bits(-1), I128s12::ZERO);
289    /// ```
290    #[inline]
291    fn not(self) -> Self {
292        Self(!self.0)
293    }
294}
295
296// -- Methods ----------------------------------------------------------
297
298impl<const SCALE: u32> I128<SCALE> {
299    /// Logical (zero-fill) right shift of the underlying `i128` storage
300    /// by `n` bits.
301    ///
302    /// Unlike the [`Shr`] operator, which is arithmetic (sign-extending),
303    /// this method reinterprets storage as `u128` for the shift, so the
304    /// vacated high bits are always filled with zeros regardless of sign.
305    ///
306    /// Operates on raw bits, not the logical decimal value.
307    ///
308    /// # Precision
309    ///
310    /// Strict: all arithmetic is integer-only; result is bit-exact.
311    ///
312    /// # Panics
313    ///
314    /// Panics in debug builds when `n >= 128`; wraps modulo 128 in
315    /// release builds.
316    ///
317    /// # Examples
318    ///
319    /// ```ignore
320    /// use decimal_scaled::I128s12;
321    /// // -1 raw is all-ones. Arithmetic shr keeps it all-ones;
322    /// // unsigned_shr clears the top bit, giving i128::MAX.
323    /// let neg_one = I128s12::from_bits(-1);
324    /// assert_eq!(neg_one >> 1u32, neg_one);                          // sign-extending
325    /// assert_eq!(neg_one.unsigned_shr(1), I128s12::from_bits(i128::MAX)); // zero-fill
326    /// ```
327    #[inline]
328    pub const fn unsigned_shr(self, n: u32) -> Self {
329        // Reinterpret storage as u128, shift, then cast back. The
330        // round-trip is bit-exact under two's complement.
331        Self(((self.0 as u128) >> n) as i128)
332    }
333
334    /// Rotate the underlying `i128` storage left by `n` bits. Bits
335    /// shifted off the high end wrap into the low end.
336    ///
337    /// Operates on raw bits, not the logical decimal value.
338    /// Delegates to [`i128::rotate_left`].
339    ///
340    /// # Precision
341    ///
342    /// Strict: all arithmetic is integer-only; result is bit-exact.
343    ///
344    /// # Examples
345    ///
346    /// ```
347    /// use decimal_scaled::I128s12;
348    /// // 0b111 rotated left by 1 = 0b1110.
349    /// assert_eq!(I128s12::from_bits(0b111).rotate_left(1), I128s12::from_bits(0b1110));
350    /// ```
351    #[inline]
352    pub const fn rotate_left(self, n: u32) -> Self {
353        Self(self.0.rotate_left(n))
354    }
355
356    /// Rotate the underlying `i128` storage right by `n` bits. Bits
357    /// shifted off the low end wrap into the high end.
358    ///
359    /// Operates on raw bits, not the logical decimal value.
360    /// Delegates to [`i128::rotate_right`].
361    ///
362    /// # Precision
363    ///
364    /// Strict: all arithmetic is integer-only; result is bit-exact.
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// use decimal_scaled::I128s12;
370    /// // 1 rotated right by 1 wraps the low bit to the top: i128::MIN.
371    /// assert_eq!(I128s12::from_bits(1).rotate_right(1), I128s12::from_bits(i128::MIN));
372    /// ```
373    #[inline]
374    pub const fn rotate_right(self, n: u32) -> Self {
375        Self(self.0.rotate_right(n))
376    }
377
378    /// Number of leading zero bits in the underlying `i128` storage.
379    ///
380    /// Returns `128` for storage value `0`, `127` for `1`, and `0` for
381    /// `from_bits(-1)` (all-ones). Delegates to [`i128::leading_zeros`].
382    ///
383    /// Operates on raw bits, not the logical decimal value.
384    ///
385    /// # Precision
386    ///
387    /// Strict: all arithmetic is integer-only; result is bit-exact.
388    ///
389    /// # Examples
390    ///
391    /// ```
392    /// use decimal_scaled::I128s12;
393    /// assert_eq!(I128s12::from_bits(1).leading_zeros(), 127);
394    /// assert_eq!(I128s12::ZERO.leading_zeros(), 128);
395    /// assert_eq!(I128s12::from_bits(-1).leading_zeros(), 0);
396    /// ```
397    #[inline]
398    pub const fn leading_zeros(self) -> u32 {
399        self.0.leading_zeros()
400    }
401
402    /// Number of trailing zero bits in the underlying `i128` storage.
403    ///
404    /// Returns `128` for storage value `0` and `3` for `from_bits(8)`.
405    /// Delegates to [`i128::trailing_zeros`].
406    ///
407    /// Operates on raw bits, not the logical decimal value.
408    ///
409    /// # Precision
410    ///
411    /// Strict: all arithmetic is integer-only; result is bit-exact.
412    ///
413    /// # Examples
414    ///
415    /// ```
416    /// use decimal_scaled::I128s12;
417    /// assert_eq!(I128s12::from_bits(8).trailing_zeros(), 3);
418    /// assert_eq!(I128s12::ZERO.trailing_zeros(), 128);
419    /// ```
420    #[inline]
421    pub const fn trailing_zeros(self) -> u32 {
422        self.0.trailing_zeros()
423    }
424
425    /// Population count: number of `1` bits set in the underlying `i128`
426    /// storage.
427    ///
428    /// Note the storage-not-value semantic: `I128s12::ONE.count_ones()`
429    /// returns the popcount of `10^12` (= 21), not `1`. Use
430    /// [`I128::from_bits`] when you need a predictable bit pattern.
431    ///
432    /// Delegates to [`i128::count_ones`].
433    ///
434    /// # Precision
435    ///
436    /// Strict: all arithmetic is integer-only; result is bit-exact.
437    ///
438    /// # Examples
439    ///
440    /// ```
441    /// use decimal_scaled::I128s12;
442    /// assert_eq!(I128s12::from_bits(0b101).count_ones(), 2);
443    /// ```
444    #[inline]
445    pub const fn count_ones(self) -> u32 {
446        self.0.count_ones()
447    }
448
449    /// Number of `0` bits in the underlying `i128` storage. Always equal
450    /// to `128 - self.count_ones()`.
451    ///
452    /// Delegates to [`i128::count_zeros`].
453    ///
454    /// Operates on raw bits, not the logical decimal value.
455    ///
456    /// # Precision
457    ///
458    /// Strict: all arithmetic is integer-only; result is bit-exact.
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// use decimal_scaled::I128s12;
464    /// // 0b101 has 2 ones and 126 zeros in 128-bit storage.
465    /// assert_eq!(I128s12::from_bits(0b101).count_zeros(), 126);
466    /// ```
467    #[inline]
468    pub const fn count_zeros(self) -> u32 {
469        self.0.count_zeros()
470    }
471
472    /// Returns `true` if the underlying `i128` storage is a power of two
473    /// (exactly one bit set and the value is positive).
474    ///
475    /// Implemented by reinterpreting the storage as `u128` and delegating
476    /// to [`u128::is_power_of_two`]. Negative `i128` values always return
477    /// `false` because the sign bit being set means more than one bit is
478    /// set in the `u128` view.
479    ///
480    /// Note the storage-not-value semantic: `I128s12::ONE.is_power_of_two()`
481    /// returns `false` because storage is `10^12 = 2^12 * 5^12`, not a
482    /// single-bit value.
483    ///
484    /// Operates on raw bits, not the logical decimal value.
485    ///
486    /// # Precision
487    ///
488    /// Strict: all arithmetic is integer-only; result is bit-exact.
489    ///
490    /// # Examples
491    ///
492    /// ```
493    /// use decimal_scaled::I128s12;
494    /// assert!(I128s12::from_bits(8).is_power_of_two());
495    /// assert!(!I128s12::from_bits(7).is_power_of_two());
496    /// assert!(!I128s12::from_bits(-1).is_power_of_two());
497    /// ```
498    #[inline]
499    pub const fn is_power_of_two(self) -> bool {
500        // Reinterpret as u128 for the canonical "exactly one bit set"
501        // check. Negative i128 has the sign bit set, so as u128 the
502        // popcount may be more than one; the check correctly returns false.
503        (self.0 as u128).is_power_of_two()
504    }
505
506    /// Smallest power of two greater than or equal to the underlying
507    /// `i128` storage, treating the storage as `u128`.
508    ///
509    /// Delegates to [`u128::next_power_of_two`] over the unsigned
510    /// reinterpretation, then casts the result back to `i128`. If the
511    /// next power of two exceeds `i128::MAX` the raw bit pattern wraps
512    /// into the negative `i128` range.
513    ///
514    /// Operates on raw bits, not the logical decimal value.
515    ///
516    /// # Precision
517    ///
518    /// Strict: all arithmetic is integer-only; result is bit-exact.
519    ///
520    /// # Panics
521    ///
522    /// Panics in debug builds when the next power of two overflows
523    /// `u128::MAX`, matching [`u128::next_power_of_two`] semantics.
524    ///
525    /// # Examples
526    ///
527    /// ```
528    /// use decimal_scaled::I128s12;
529    /// assert_eq!(I128s12::from_bits(7).next_power_of_two(), I128s12::from_bits(8));
530    /// assert_eq!(I128s12::from_bits(8).next_power_of_two(), I128s12::from_bits(8));
531    /// ```
532    #[inline]
533    pub const fn next_power_of_two(self) -> Self {
534        Self((self.0 as u128).next_power_of_two() as i128)
535    }
536}
537
538// -- Tests ------------------------------------------------------------
539
540#[cfg(test)]
541mod tests {
542    use super::*;
543    use crate::core_type::I128s12;
544
545    // --- BitAnd / BitOr / BitXor ------------------------------------
546
547    #[test]
548    fn bitand_clears_bits() {
549        // raw-bit boundary; from_bits not ONE
550        let a = I128s12::from_bits(0xF0);
551        let b = I128s12::from_bits(0x0F);
552        assert_eq!(a & b, I128s12::from_bits(0x00));
553    }
554
555    #[test]
556    fn bitand_assign_in_place() {
557        let mut a = I128s12::from_bits(0xFF);
558        a &= I128s12::from_bits(0x0F);
559        assert_eq!(a, I128s12::from_bits(0x0F));
560    }
561
562    #[test]
563    fn bitor_sets_bits() {
564        // raw-bit boundary; from_bits not ONE
565        let zero = I128s12::ZERO;
566        let one_lsb = I128s12::from_bits(1);
567        assert_eq!(zero | one_lsb, one_lsb);
568    }
569
570    #[test]
571    fn bitor_assign_in_place() {
572        let mut a = I128s12::from_bits(0xF0);
573        a |= I128s12::from_bits(0x0F);
574        assert_eq!(a, I128s12::from_bits(0xFF));
575    }
576
577    #[test]
578    fn bitxor_toggles_bits() {
579        let a = I128s12::from_bits(0b1100);
580        let b = I128s12::from_bits(0b1010);
581        assert_eq!(a ^ b, I128s12::from_bits(0b0110));
582    }
583
584    #[test]
585    fn bitxor_assign_in_place() {
586        let mut a = I128s12::from_bits(0xFF);
587        a ^= I128s12::from_bits(0x0F);
588        assert_eq!(a, I128s12::from_bits(0xF0));
589    }
590
591    #[test]
592    fn bitxor_self_is_zero() {
593        let a = I128s12::from_bits(0xDEAD_BEEF_i128);
594        assert_eq!(a ^ a, I128s12::ZERO);
595    }
596
597    // --- Shl / Shr ---------------------------------------------------
598
599    #[test]
600    fn shl_doubles_lsb() {
601        // raw-bit boundary; from_bits(1) not ONE
602        assert_eq!(I128s12::from_bits(1) << 1u32, I128s12::from_bits(2));
603    }
604
605    #[test]
606    fn shr_halves_lsb() {
607        // raw-bit boundary; from_bits not ONE
608        assert_eq!(I128s12::from_bits(2) >> 1u32, I128s12::from_bits(1));
609    }
610
611    #[test]
612    fn shr_is_sign_extending() {
613        // -1 raw is all-ones; arithmetic shr preserves all-ones.
614        assert_eq!(I128s12::from_bits(-1) >> 1u32, I128s12::from_bits(-1));
615    }
616
617    #[test]
618    fn shr_negative_stays_negative() {
619        // -8 raw >> 1 = -4 raw under arithmetic shift.
620        assert_eq!(I128s12::from_bits(-8) >> 1u32, I128s12::from_bits(-4));
621    }
622
623    #[test]
624    fn shl_assign_in_place() {
625        let mut a = I128s12::from_bits(1);
626        a <<= 4u32;
627        assert_eq!(a, I128s12::from_bits(16));
628    }
629
630    #[test]
631    fn shr_assign_in_place() {
632        let mut a = I128s12::from_bits(16);
633        a >>= 2u32;
634        assert_eq!(a, I128s12::from_bits(4));
635    }
636
637    // --- Not ---------------------------------------------------------
638
639    #[test]
640    fn not_zero_is_neg_one() {
641        // raw-bit boundary; from_bits(-1) not -ONE
642        assert_eq!(!I128s12::ZERO, I128s12::from_bits(-1));
643    }
644
645    #[test]
646    fn not_neg_one_is_zero() {
647        assert_eq!(!I128s12::from_bits(-1), I128s12::ZERO);
648    }
649
650    #[test]
651    fn not_is_self_inverse() {
652        let a = I128s12::from_bits(0xCAFE);
653        assert_eq!(!!a, a);
654    }
655
656    // --- unsigned_shr ------------------------------------------------
657
658    #[test]
659    fn unsigned_shr_zero_fills_negative() {
660        // -1 raw is all-ones; logical shr by 1 clears the top bit, so
661        // the result is i128::MAX.
662        assert_eq!(
663            I128s12::from_bits(-1).unsigned_shr(1),
664            I128s12::from_bits(i128::MAX)
665        );
666    }
667
668    #[test]
669    fn unsigned_shr_positive_matches_arithmetic_shr() {
670        // For non-negative inputs, arithmetic and logical shifts agree.
671        let a = I128s12::from_bits(0xFF);
672        assert_eq!(a.unsigned_shr(4), a >> 4u32);
673        assert_eq!(a.unsigned_shr(4), I128s12::from_bits(0x0F));
674    }
675
676    #[test]
677    fn unsigned_shr_zero_amount_identity() {
678        let a = I128s12::from_bits(-42);
679        assert_eq!(a.unsigned_shr(0), a);
680    }
681
682    // --- rotate_left / rotate_right ---------------------------------
683
684    #[test]
685    fn rotate_left_low_bits() {
686        // 0b111 rotate_left 1 = 0b1110 = 14.
687        assert_eq!(
688            I128s12::from_bits(0b111).rotate_left(1),
689            I128s12::from_bits(0b1110)
690        );
691    }
692
693    #[test]
694    fn rotate_right_low_bit_wraps_to_top() {
695        // 1 rotate_right 1 = top bit set = i128::MIN raw.
696        assert_eq!(
697            I128s12::from_bits(1).rotate_right(1),
698            I128s12::from_bits(i128::MIN)
699        );
700    }
701
702    #[test]
703    fn rotate_left_full_width_is_identity() {
704        let a = I128s12::from_bits(0xDEAD_BEEF_i128);
705        assert_eq!(a.rotate_left(128), a);
706    }
707
708    #[test]
709    fn rotate_right_round_trip() {
710        let a = I128s12::from_bits(0xCAFE_F00D_i128);
711        assert_eq!(a.rotate_left(13).rotate_right(13), a);
712    }
713
714    // --- leading_zeros / trailing_zeros -----------------------------
715
716    #[test]
717    fn leading_zeros_lsb_is_127() {
718        // raw-bit boundary; from_bits(1) not ONE
719        assert_eq!(I128s12::from_bits(1).leading_zeros(), 127);
720    }
721
722    #[test]
723    fn leading_zeros_zero_is_128() {
724        assert_eq!(I128s12::ZERO.leading_zeros(), 128);
725    }
726
727    #[test]
728    fn leading_zeros_neg_one_is_zero() {
729        assert_eq!(I128s12::from_bits(-1).leading_zeros(), 0);
730    }
731
732    #[test]
733    fn trailing_zeros_eight_is_three() {
734        assert_eq!(I128s12::from_bits(8).trailing_zeros(), 3);
735    }
736
737    #[test]
738    fn trailing_zeros_zero_is_128() {
739        assert_eq!(I128s12::ZERO.trailing_zeros(), 128);
740    }
741
742    #[test]
743    fn trailing_zeros_one_is_zero() {
744        assert_eq!(I128s12::from_bits(1).trailing_zeros(), 0);
745    }
746
747    // --- count_ones / count_zeros -----------------------------------
748
749    #[test]
750    fn count_ones_pattern() {
751        // 0b101 has two ones.
752        assert_eq!(I128s12::from_bits(0b101).count_ones(), 2);
753    }
754
755    #[test]
756    fn count_zeros_pattern() {
757        // 0b101 has 128 - 2 = 126 zeros (in i128 storage).
758        assert_eq!(I128s12::from_bits(0b101).count_zeros(), 126);
759    }
760
761    #[test]
762    fn count_ones_zero_is_zero() {
763        assert_eq!(I128s12::ZERO.count_ones(), 0);
764    }
765
766    #[test]
767    fn count_ones_neg_one_is_128() {
768        // -1 raw is all-ones.
769        assert_eq!(I128s12::from_bits(-1).count_ones(), 128);
770    }
771
772    #[test]
773    fn count_zeros_complement_relation() {
774        // count_ones + count_zeros == 128 for every value.
775        let a = I128s12::from_bits(0xDEAD_BEEF_CAFE_i128);
776        assert_eq!(a.count_ones() + a.count_zeros(), 128);
777    }
778
779    // --- is_power_of_two / next_power_of_two ------------------------
780
781    #[test]
782    fn is_power_of_two_true_for_eight() {
783        assert!(I128s12::from_bits(8).is_power_of_two());
784    }
785
786    #[test]
787    fn is_power_of_two_false_for_seven() {
788        assert!(!I128s12::from_bits(7).is_power_of_two());
789    }
790
791    #[test]
792    fn is_power_of_two_false_for_zero() {
793        assert!(!I128s12::ZERO.is_power_of_two());
794    }
795
796    #[test]
797    fn is_power_of_two_false_for_negative() {
798        // Negative i128 has the sign bit set; reinterpreted as u128 the
799        // popcount is more than one, so not a power of two.
800        assert!(!I128s12::from_bits(-1).is_power_of_two());
801    }
802
803    #[test]
804    fn is_power_of_two_storage_not_value_semantic() {
805        // I128s12::ONE has storage 10^12 = 2^12 * 5^12, not a power of
806        // two. Documents the storage-not-value semantic.
807        assert!(!I128s12::ONE.is_power_of_two());
808    }
809
810    #[test]
811    fn next_power_of_two_seven_is_eight() {
812        assert_eq!(
813            I128s12::from_bits(7).next_power_of_two(),
814            I128s12::from_bits(8)
815        );
816    }
817
818    #[test]
819    fn next_power_of_two_eight_is_eight() {
820        // Already a power of two -- stays put.
821        assert_eq!(
822            I128s12::from_bits(8).next_power_of_two(),
823            I128s12::from_bits(8)
824        );
825    }
826
827    #[test]
828    fn next_power_of_two_one_is_one() {
829        assert_eq!(
830            I128s12::from_bits(1).next_power_of_two(),
831            I128s12::from_bits(1)
832        );
833    }
834
835    // --- Const-generic exercise at a non-default scale --------------
836
837    #[test]
838    fn ops_work_at_scale_six() {
839        type D6 = I128<6>;
840        let a = D6::from_bits(0b1100);
841        let b = D6::from_bits(0b1010);
842        assert_eq!(a & b, D6::from_bits(0b1000));
843        assert_eq!(a | b, D6::from_bits(0b1110));
844        assert_eq!(a ^ b, D6::from_bits(0b0110));
845        assert_eq!(D6::from_bits(1) << 3u32, D6::from_bits(8));
846        assert_eq!(D6::from_bits(8) >> 3u32, D6::from_bits(1));
847        assert_eq!(!D6::ZERO, D6::from_bits(-1));
848        assert_eq!(D6::from_bits(8).count_ones(), 1);
849        assert!(D6::from_bits(8).is_power_of_two());
850    }
851}