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}