1use std::{
5 fmt,
6 ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr},
7};
8
9use binius_utils::serialization::{DeserializeBytes, SerializationError, SerializeBytes};
10use bytes::{Buf, BufMut};
11
12#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct Word(pub u64);
16
17impl Word {
18 pub const ZERO: Word = Word(0);
20 pub const ONE: Word = Word(1);
22 pub const ALL_ONE: Word = Word(u64::MAX);
24 pub const MASK_32: Word = Word(0x00000000FFFFFFFF);
26 pub const MSB_ONE: Word = Word(0x8000000000000000);
30}
31
32impl fmt::Debug for Word {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 write!(f, "Word({:#018x})", self.0)
35 }
36}
37
38impl BitAnd for Word {
39 type Output = Self;
40
41 fn bitand(self, rhs: Self) -> Self::Output {
42 Word(self.0 & rhs.0)
43 }
44}
45
46impl BitOr for Word {
47 type Output = Self;
48
49 fn bitor(self, rhs: Self) -> Self::Output {
50 Word(self.0 | rhs.0)
51 }
52}
53
54impl BitXor for Word {
55 type Output = Self;
56
57 fn bitxor(self, rhs: Self) -> Self::Output {
58 Word(self.0 ^ rhs.0)
59 }
60}
61
62impl Shl<u32> for Word {
63 type Output = Self;
64
65 fn shl(self, rhs: u32) -> Self::Output {
66 Word(self.0 << rhs)
67 }
68}
69
70impl Shr<u32> for Word {
71 type Output = Self;
72
73 fn shr(self, rhs: u32) -> Self::Output {
74 Word(self.0 >> rhs)
75 }
76}
77
78impl Not for Word {
79 type Output = Self;
80
81 fn not(self) -> Self::Output {
82 Word(!self.0)
83 }
84}
85
86impl Word {
87 pub fn from_u64(value: u64) -> Word {
89 Word(value)
90 }
91
92 pub fn iadd32_cin_cout(self, rhs: Word, cin: Word) -> (Word, Word) {
101 let Word(lhs) = self;
102 let Word(rhs) = rhs;
103 let Word(cin) = cin;
104
105 let cin_lo = (cin >> 31) & 1;
107 let cin_hi = (cin >> 63) & 1;
108
109 let lo_l = lhs as u32;
111 let hi_l = (lhs >> 32) as u32;
112 let lo_r = rhs as u32;
113 let hi_r = (rhs >> 32) as u32;
114
115 let lo_sum = (lo_l as u64) + (lo_r as u64) + cin_lo;
117 let hi_sum = (hi_l as u64) + (hi_r as u64) + cin_hi;
118 let sum = (lo_sum as u32 as u64) | ((hi_sum as u32 as u64) << 32);
119
120 let cout = (lhs & rhs) | ((lhs ^ rhs) & !sum);
121 (Word(sum), Word(cout))
122 }
123
124 pub fn iadd_cout_32(self, rhs: Word) -> (Word, Word) {
128 self.iadd32_cin_cout(rhs, Word::ZERO)
129 }
130
131 pub fn iadd_cin_cout(self, rhs: Word, cin: Word) -> (Word, Word) {
139 debug_assert!(cin == Word::ZERO || cin == Word::ONE, "cin must be 0 or 1");
140 let Word(lhs) = self;
141 let Word(rhs) = rhs;
142 let Word(cin) = cin;
143 let sum = lhs.wrapping_add(rhs).wrapping_add(cin);
144 let cout = (lhs & rhs) | ((lhs ^ rhs) & !sum);
145 (Word(sum), Word(cout))
146 }
147
148 pub fn isub_bin_bout(self, rhs: Word, bin: Word) -> (Word, Word) {
156 debug_assert!(bin == Word::ZERO || bin == Word::ONE, "bin must be 0 or 1");
157 let Word(lhs) = self;
158 let Word(rhs) = rhs;
159 let Word(bin) = bin;
160 let diff = lhs.wrapping_sub(rhs).wrapping_sub(bin);
161 let bout = (!lhs & rhs) | (!(lhs ^ rhs) & diff);
162 (Word(diff), Word(bout))
163 }
164
165 pub fn shr_32(self, n: u32) -> Word {
167 let Word(value) = self;
168 let result = (value >> n) & 0x00000000_FFFFFFFF;
170 Word(result)
171 }
172
173 pub fn sar(&self, n: u32) -> Word {
177 let Word(value) = self;
178 let value = *value as i64;
179 let result = value >> n;
180 Word(result as u64)
181 }
182
183 pub fn rotr(self, n: u32) -> Word {
185 let Word(value) = self;
186 Word(value.rotate_right(n))
187 }
188
189 pub fn sll32(self, n: u32) -> Word {
194 let Word(value) = self;
195 let n = n & 0x1F; let lo = value as u32;
199 let hi = (value >> 32) as u32;
200
201 let lo_shifted = (lo << n) as u64;
203 let hi_shifted = ((hi << n) as u64) << 32;
204
205 Word(lo_shifted | hi_shifted)
206 }
207
208 pub fn srl32(self, n: u32) -> Word {
213 let Word(value) = self;
214 let n = n & 0x1F; let lo = value as u32;
218 let hi = (value >> 32) as u32;
219
220 let lo_shifted = (lo >> n) as u64;
222 let hi_shifted = ((hi >> n) as u64) << 32;
223
224 Word(lo_shifted | hi_shifted)
225 }
226
227 pub fn sra32(self, n: u32) -> Word {
233 let Word(value) = self;
234 let n = n & 0x1F; let lo = value as u32 as i32;
238 let hi = (value >> 32) as u32 as i32;
239
240 let lo_shifted = ((lo >> n) as u32) as u64;
242 let hi_shifted = (((hi >> n) as u32) as u64) << 32;
243
244 Word(lo_shifted | hi_shifted)
245 }
246
247 pub fn rotr32(self, n: u32) -> Word {
253 let Word(value) = self;
254 let n = n & 0x1F; let lo = value as u32;
258 let hi = (value >> 32) as u32;
259
260 let lo_rotated = lo.rotate_right(n) as u64;
262 let hi_rotated = (hi.rotate_right(n) as u64) << 32;
263
264 Word(lo_rotated | hi_rotated)
265 }
266
267 pub fn imul(self, rhs: Word) -> (Word, Word) {
272 let Word(lhs) = self;
273 let Word(rhs) = rhs;
274 let result = (lhs as u128) * (rhs as u128);
275
276 let hi = (result >> 64) as u64;
277 let lo = result as u64;
278 (Word(hi), Word(lo))
279 }
280
281 pub fn smul(self, rhs: Word) -> (Word, Word) {
286 let Word(lhs) = self;
287 let Word(rhs) = rhs;
288 let a = lhs as i64;
290 let b = rhs as i64;
291 let result = (a as i128) * (b as i128);
293 let hi = (result >> 64) as u64;
295 let lo = result as u64;
296 (Word(hi), Word(lo))
297 }
298
299 pub fn wrapping_add(self, rhs: Word) -> Word {
303 Word(self.0.wrapping_add(rhs.0))
304 }
305
306 pub fn wrapping_sub(self, rhs: Word) -> Word {
310 Word(self.0.wrapping_sub(rhs.0))
311 }
312
313 pub fn as_u64(self) -> u64 {
315 self.0
316 }
317
318 pub fn is_msb_true(self) -> bool {
325 (self.0 & 0x8000000000000000) != 0
326 }
327
328 pub fn is_msb_false(self) -> bool {
335 (self.0 & 0x8000000000000000) == 0
336 }
337}
338
339impl SerializeBytes for Word {
340 fn serialize(&self, write_buf: impl BufMut) -> Result<(), SerializationError> {
341 self.0.serialize(write_buf)
342 }
343}
344
345impl DeserializeBytes for Word {
346 fn deserialize(read_buf: impl Buf) -> Result<Self, SerializationError>
347 where
348 Self: Sized,
349 {
350 Ok(Word(u64::deserialize(read_buf)?))
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use proptest::prelude::*;
357
358 use super::*;
359
360 #[test]
361 fn test_constants() {
362 assert_eq!(Word::ZERO, Word(0));
363 assert_eq!(Word::ONE, Word(1));
364 assert_eq!(Word::ALL_ONE, Word(0xFFFFFFFFFFFFFFFF));
365 assert_eq!(Word::MASK_32, Word(0x00000000FFFFFFFF));
366 assert_eq!(Word::MSB_ONE, Word(0x8000000000000000));
367 }
368
369 #[test]
370 fn test_msb_bool() {
371 assert!(Word::MSB_ONE.is_msb_true());
373 assert!(!Word::MSB_ONE.is_msb_false());
374
375 assert!(!Word::ZERO.is_msb_true());
377 assert!(Word::ZERO.is_msb_false());
378
379 assert!(Word(0x8000000000000000).is_msb_true());
381 assert!(Word(0x8000000000000001).is_msb_true());
382 assert!(Word(0x80000000FFFFFFFF).is_msb_true());
383 assert!(Word(0xFFFFFFFFFFFFFFFF).is_msb_true());
384
385 assert!(Word(0x7FFFFFFFFFFFFFFF).is_msb_false());
387 assert!(Word(0x0000000000000001).is_msb_false());
388 assert!(Word(0x00000000FFFFFFFF).is_msb_false());
389 assert!(Word(0x7000000000000000).is_msb_false());
390
391 let test_word = Word(0x8123456789ABCDEF);
393 assert!(test_word.is_msb_true());
394 assert!(!test_word.is_msb_false());
395
396 let test_word2 = Word(0x7123456789ABCDEF);
397 assert!(!test_word2.is_msb_true());
398 assert!(test_word2.is_msb_false());
399 }
400
401 proptest! {
402 #[test]
403 fn prop_msb_bool(val in any::<u64>()) {
404 let word = Word(val);
405
406 assert_eq!(word.is_msb_true(), !word.is_msb_false());
408 assert_eq!(word.is_msb_false(), !word.is_msb_true());
409
410 let msb_set = (val & 0x8000000000000000) != 0;
412 assert_eq!(word.is_msb_true(), msb_set);
413 assert_eq!(word.is_msb_false(), !msb_set);
414
415 let word_with_msb = Word(val | 0x8000000000000000);
417 let word_without_msb = Word(val & 0x7FFFFFFFFFFFFFFF);
418 assert!(word_with_msb.is_msb_true());
419 assert!(word_without_msb.is_msb_false());
420 }
421
422 #[test]
423 fn prop_bitwise_and(a in any::<u64>(), b in any::<u64>()) {
424 let wa = Word(a);
425 let wb = Word(b);
426
427 assert_eq!((wa & wb).0, a & b);
429 assert_eq!(wa & Word::ALL_ONE, wa);
430 assert_eq!(wa & Word::ZERO, Word::ZERO);
431 assert_eq!(wa & wa, wa); assert_eq!(wa & wb, wb & wa);
435 }
436
437 #[test]
438 fn prop_bitwise_or(a in any::<u64>(), b in any::<u64>()) {
439 let wa = Word(a);
440 let wb = Word(b);
441
442 assert_eq!((wa | wb).0, a | b);
444 assert_eq!(wa | Word::ZERO, wa);
445 assert_eq!(wa | Word::ALL_ONE, Word::ALL_ONE);
446 assert_eq!(wa | wa, wa); assert_eq!(wa | wb, wb | wa);
450 }
451
452 #[test]
453 fn prop_bitwise_xor(a in any::<u64>(), b in any::<u64>()) {
454 let wa = Word(a);
455 let wb = Word(b);
456
457 assert_eq!((wa ^ wb).0, a ^ b);
459 assert_eq!(wa ^ Word::ZERO, wa);
460 assert_eq!(wa ^ wa, Word::ZERO);
461 assert_eq!(wa ^ Word::ALL_ONE, !wa);
462
463 assert_eq!(wa ^ wb, wb ^ wa);
465
466 assert_eq!(wa ^ wb ^ wb, wa);
468 }
469
470 #[test]
471 fn prop_bitwise_not(a in any::<u64>()) {
472 let wa = Word(a);
473
474 assert_eq!((!wa).0, !a);
476 assert_eq!(!(!wa), wa); assert_eq!(!Word::ZERO, Word::ALL_ONE);
478 assert_eq!(!Word::ALL_ONE, Word::ZERO);
479
480 let wb = Word(a.wrapping_add(1));
482 assert_eq!(!(wa & wb), !wa | !wb);
483 assert_eq!(!(wa | wb), !wa & !wb);
484 }
485
486 #[test]
487 fn prop_shift_left(val in any::<u64>(), shift in 0u32..64) {
488 let w = Word(val);
489 assert_eq!((w << shift).0, val << shift);
490
491 assert_eq!(w << 0, w);
493
494 if shift >= 64 {
496 assert_eq!((w << shift).0, 0);
497 }
498 }
499
500 #[test]
501 fn prop_shift_right(val in any::<u64>(), shift in 0u32..64) {
502 let w = Word(val);
503 assert_eq!((w >> shift).0, val >> shift);
504
505 assert_eq!(w >> 0, w);
507
508 if shift >= 64 {
510 assert_eq!((w >> shift).0, 0);
511 }
512 }
513
514 #[test]
515 fn prop_shift_inverse(val in any::<u64>(), shift in 1u32..64) {
516 let w = Word(val);
517 let mask = (1u64 << (64 - shift)) - 1;
519 assert_eq!(((w << shift) >> shift).0, val & mask);
520
521 let high_mask = !((1u64 << shift) - 1);
523 assert_eq!(((w >> shift) << shift).0, val & high_mask);
524 }
525
526 #[test]
527 fn prop_sar(val in any::<u64>(), shift in 0u32..64) {
528 let w = Word(val);
529 let expected = ((val as i64) >> shift) as u64;
530 assert_eq!(w.sar(shift).0, expected);
531
532 assert_eq!(w.sar(0), w);
534
535 let sign_extended = if (val as i64) < 0 {
537 Word(0xFFFFFFFFFFFFFFFF)
538 } else {
539 Word(0)
540 };
541 assert_eq!(w.sar(63), sign_extended);
542 }
543
544 #[test]
545 fn prop_sar_sign_extension(val in any::<u64>(), shift in 1u32..64) {
546 let w = Word(val);
547 let result = w.sar(shift);
548
549 let is_negative = (val as i64) < 0;
551 if is_negative {
552 let mask = !((1u64 << (64 - shift)) - 1);
554 assert_eq!(result.0 & mask, mask);
555 } else {
556 let mask = !((1u64 << (64 - shift)) - 1);
558 assert_eq!(result.0 & mask, 0);
559 }
560 }
561
562 #[test]
563 fn prop_iadd32_cin_cout(
564 a in any::<u64>(), b in any::<u64>(),
565 cin_lo in proptest::bool::ANY, cin_hi in proptest::bool::ANY,
566 ) {
567 let cin_word = ((cin_lo as u64) << 31) | ((cin_hi as u64) << 63);
569 let wa = Word(a);
570 let wb = Word(b);
571 let wcin = Word(cin_word);
572 let (sum, cout) = wa.iadd32_cin_cout(wb, wcin);
573
574 let lo_sum = (a as u32 as u64) + (b as u32 as u64) + (cin_lo as u64);
576 let hi_sum = ((a >> 32) as u32 as u64) + ((b >> 32) as u32 as u64) + (cin_hi as u64);
577 let expected_sum = (lo_sum as u32 as u64) | ((hi_sum as u32 as u64) << 32);
578 assert_eq!(sum.0, expected_sum);
579
580 let expected_cout = (a & b) | ((a ^ b) & !expected_sum);
582 assert_eq!(cout.0, expected_cout);
583
584 let (sum0, cout0) = wa.iadd_cout_32(wb);
586 let (sum1, cout1) = wa.iadd32_cin_cout(wb, Word::ZERO);
587 assert_eq!(sum0, sum1);
588 assert_eq!(cout0, cout1);
589 }
590
591 #[test]
592 fn prop_iadd_cin_cout(a in any::<u64>(), b in any::<u64>(), cin in 0u64..=1) {
593 let wa = Word(a);
594 let wb = Word(b);
595 let wcin = Word(cin);
596 let (sum, cout) = wa.iadd_cin_cout(wb, wcin);
597
598 let expected_sum = a.wrapping_add(b).wrapping_add(cin);
600 assert_eq!(sum.0, expected_sum);
601
602 let expected_cout = (a & b) | ((a ^ b) & !expected_sum);
604 assert_eq!(cout.0, expected_cout);
605
606 let (sum0, cout0) = wa.iadd_cin_cout(wb, Word::ZERO);
608 let full_sum = a.wrapping_add(b);
609 assert_eq!(sum0.0, full_sum);
610 assert_eq!(cout0.0, (a & b) | ((a ^ b) & !full_sum));
611 }
612
613 #[test]
614 fn prop_isub_bin_bout(a in any::<u64>(), b in any::<u64>(), bin in 0u64..=1) {
615 let wa = Word(a);
616 let wb = Word(b);
617 let wbin = Word(bin);
618 let (diff, bout) = wa.isub_bin_bout(wb, wbin);
619
620 let expected_diff = a.wrapping_sub(b).wrapping_sub(bin);
622 assert_eq!(diff.0, expected_diff);
623
624 let expected_bout = (!a & b) | (!(a ^ b) & expected_diff);
626 assert_eq!(bout.0, expected_bout);
627
628 let (diff0, bout0) = wa.isub_bin_bout(wb, Word::ZERO);
630 let expected = a.wrapping_sub(b);
631 assert_eq!(diff0.0, expected);
632 assert_eq!(bout0.0, (!a & b) | (!(a ^ b) & expected));
633 }
634
635 #[test]
636 fn prop_shr_32(val in any::<u64>(), shift in 0u32..64) {
637 let w = Word(val);
638 let result = w.shr_32(shift);
639
640 let expected = (val >> shift) & 0xFFFFFFFF;
642 assert_eq!(result.0, expected);
643
644 assert_eq!(w.shr_32(0).0, val & 0xFFFFFFFF);
646
647 if shift >= 32 {
649 assert_eq!(result.0, (val >> shift) & 0xFFFFFFFF);
650 }
651 }
652 #[test]
653 fn prop_rotr(val in any::<u64>(), rotate in 0u32..128) {
654 let w = Word(val);
655 let result = w.rotr(rotate);
656
657 let rotate_mod = rotate % 64;
659 let expected = val.rotate_right(rotate_mod);
660 assert_eq!(result.0, expected);
661
662 assert_eq!(w.rotr(0), w);
664 assert_eq!(w.rotr(64), w);
665
666 let r1 = rotate % 64;
668 let r2 = (64 - r1) % 64;
669 if r1 != 0 {
670 assert_eq!(w.rotr(r1).rotr(r2), w);
671 }
672 }
673
674 #[test]
675 fn prop_imul(a in any::<u64>(), b in any::<u64>()) {
676 let wa = Word(a);
677 let wb = Word(b);
678 let (hi, lo) = wa.imul(wb);
679
680 let result = (a as u128) * (b as u128);
682 assert_eq!(hi.0, (result >> 64) as u64);
683 assert_eq!(lo.0, result as u64);
684
685 let (hi0, lo0) = wa.imul(Word::ZERO);
687 assert_eq!(hi0, Word::ZERO);
688 assert_eq!(lo0, Word::ZERO);
689
690 let (hi1, lo1) = wa.imul(Word::ONE);
692 assert_eq!(hi1, Word::ZERO);
693 assert_eq!(lo1, wa);
694
695 let (hi_ab, lo_ab) = wa.imul(wb);
697 let (hi_reversed, lo_reversed) = wb.imul(wa);
698 assert_eq!(hi_ab, hi_reversed);
699 assert_eq!(lo_ab, lo_reversed);
700 }
701
702 #[test]
703 fn prop_sll32(val in any::<u64>(), shift in 0u32..32) {
704 let w = Word(val);
705 let result = w.sll32(shift);
706
707 let lo = val as u32;
709 let hi = (val >> 32) as u32;
710
711 let expected_lo = ((lo << shift) as u64) & 0xFFFFFFFF;
713 let expected_hi = ((hi << shift) as u64) << 32;
714 let expected = expected_lo | expected_hi;
715
716 assert_eq!(result.0, expected);
717
718 assert_eq!(w.sll32(0), w);
720
721 let w_test = Word(0x40000001_40000001);
723 let result_31 = w_test.sll32(31);
724 assert_eq!(result_31.0, 0x80000000_80000000);
725
726 assert_eq!(w.sll32(shift), w.sll32(shift | 0x20));
728 }
729
730 #[test]
731 fn prop_srl32(val in any::<u64>(), shift in 0u32..32) {
732 let w = Word(val);
733 let result = w.srl32(shift);
734
735 let lo = val as u32;
737 let hi = (val >> 32) as u32;
738
739 let expected_lo = (lo >> shift) as u64;
741 let expected_hi = ((hi >> shift) as u64) << 32;
742 let expected = expected_lo | expected_hi;
743
744 assert_eq!(result.0, expected);
745
746 assert_eq!(w.srl32(0), w);
748
749 let w_test = Word(0x80000000_80000000);
751 let result_31 = w_test.srl32(31);
752 assert_eq!(result_31.0, 0x00000001_00000001);
753
754 assert_eq!(w.srl32(shift), w.srl32(shift | 0x20));
756 }
757
758 #[test]
759 fn prop_sra32(val in any::<u64>(), shift in 0u32..32) {
760 let w = Word(val);
761 let result = w.sra32(shift);
762
763 let lo = val as u32 as i32;
765 let hi = (val >> 32) as u32 as i32;
766
767 let expected_lo = ((lo >> shift) as u32) as u64;
769 let expected_hi = (((hi >> shift) as u32) as u64) << 32;
770 let expected = expected_lo | expected_hi;
771
772 assert_eq!(result.0, expected);
773
774 assert_eq!(w.sra32(0), w);
776
777 let w_neg = Word(0x80000000_80000000);
779 let result_1 = w_neg.sra32(1);
780 assert_eq!(result_1.0, 0xC0000000_C0000000);
781
782 let w_pos = Word(0x40000000_40000000);
784 let result_1_pos = w_pos.sra32(1);
785 assert_eq!(result_1_pos.0, 0x20000000_20000000);
786
787 let result_31 = w.sra32(31);
789 let expected_lo_31 = if lo < 0 { 0xFFFFFFFF } else { 0 };
790 let expected_hi_31 = if hi < 0 { 0xFFFFFFFF00000000 } else { 0 };
791 assert_eq!(result_31.0, expected_lo_31 | expected_hi_31);
792
793 assert_eq!(w.sra32(shift), w.sra32(shift | 0x20));
795 }
796
797 #[test]
798 fn prop_rotr32(val in any::<u64>(), rotate in 0u32..32) {
799 let w = Word(val);
800 let result = w.rotr32(rotate);
801
802 let lo = val as u32;
804 let hi = (val >> 32) as u32;
805
806 let expected_lo = lo.rotate_right(rotate) as u64;
808 let expected_hi = ((hi.rotate_right(rotate)) as u64) << 32;
809 let expected = expected_lo | expected_hi;
810
811 assert_eq!(result.0, expected);
812
813 assert_eq!(w.rotr32(0), w);
815
816 assert_eq!(w.rotr32(32), w.rotr32(0));
818
819 assert_eq!(w.rotr32(rotate), w.rotr32(rotate | 0x20));
821
822 if rotate > 0 && rotate < 32 {
824 let w_test = Word(0x12345678_9ABCDEF0);
825 let rotated = w_test.rotr32(rotate);
826 let back = rotated.rotr32(32 - rotate);
827 assert_eq!(back, w_test);
828 }
829 }
830
831 #[test]
832 fn prop_smul(a in any::<u64>(), b in any::<u64>()) {
833 let wa = Word(a);
834 let wb = Word(b);
835 let (hi, lo) = wa.smul(wb);
836
837 let result = (a as i64 as i128) * (b as i64 as i128);
839 assert_eq!(hi.0, (result >> 64) as u64);
840 assert_eq!(lo.0, result as u64);
841
842 let (hi0, lo0) = wa.smul(Word::ZERO);
844 assert_eq!(hi0, Word::ZERO);
845 assert_eq!(lo0, Word::ZERO);
846
847 let (hi1, lo1) = wa.smul(Word::ONE);
849 let expected_hi = if (a as i64) < 0 { Word(0xFFFFFFFFFFFFFFFF) } else { Word::ZERO };
850 assert_eq!(hi1, expected_hi);
851 assert_eq!(lo1, wa);
852
853 let (hi_neg, lo_neg) = wa.smul(Word(0xFFFFFFFFFFFFFFFF));
855 let neg_result = -(a as i64 as i128);
856 assert_eq!(hi_neg.0, (neg_result >> 64) as u64);
857 assert_eq!(lo_neg.0, neg_result as u64);
858
859 let (hi_ab, lo_ab) = wa.smul(wb);
861 let (hi_reversed, lo_reversed) = wb.smul(wa);
862 assert_eq!(hi_ab, hi_reversed);
863 assert_eq!(lo_ab, lo_reversed);
864 }
865
866 #[test]
867 fn prop_wrapping_sub(a in any::<u64>(), b in any::<u64>()) {
868 let wa = Word(a);
869 let wb = Word(b);
870 let result = wa.wrapping_sub(wb);
871
872 assert_eq!(result.0, a.wrapping_sub(b));
873
874 assert_eq!(wa.wrapping_sub(Word::ZERO), wa);
876
877 assert_eq!(wa.wrapping_sub(wa), Word::ZERO);
879
880 let sum = Word(a.wrapping_add(b));
882 assert_eq!(sum.wrapping_sub(wb), wa);
883 }
884
885 #[test]
886 fn prop_conversions(val in any::<u64>()) {
887 let word = Word::from_u64(val);
888 assert_eq!(word.as_u64(), val);
889 assert_eq!(word, Word(val));
890
891 assert_eq!(Word::from_u64(word.as_u64()), word);
893 }
894
895 #[test]
896 fn prop_debug_format(val in any::<u64>()) {
897 let word = Word(val);
898 let debug_str = format!("{:?}", word);
899 assert!(debug_str.starts_with("Word(0x"));
900 assert!(debug_str.ends_with(")"));
901 let expected = format!("Word({:#018x})", val);
903 assert_eq!(debug_str, expected);
904 }
905 }
906
907 #[test]
908 fn test_32bit_shift_edge_cases() {
909 let w1 = Word(0x12345678_9ABCDEF0);
911 assert_eq!(w1.sll32(4).0, 0x23456780_ABCDEF00);
912 assert_eq!(w1.sll32(16).0, 0x56780000_DEF00000);
913
914 let w2 = Word(0xFFFFFFFF_00000000);
916 assert_eq!(w2.sll32(1).0, 0xFFFFFFFE_00000000);
917 let w3 = Word(0x00000000_FFFFFFFF);
918 assert_eq!(w3.sll32(1).0, 0x00000000_FFFFFFFE);
919
920 assert_eq!(w1.srl32(4).0, 0x01234567_09ABCDEF);
922 assert_eq!(w1.srl32(16).0, 0x00001234_00009ABC);
923
924 let w4 = Word(0x80000000_7FFFFFFF); assert_eq!(w4.sra32(1).0, 0xC0000000_3FFFFFFF);
927 assert_eq!(w4.sra32(31).0, 0xFFFFFFFF_00000000);
928
929 let w5 = Word(0x7FFFFFFF_80000000); assert_eq!(w5.sra32(1).0, 0x3FFFFFFF_C0000000);
931 assert_eq!(w5.sra32(31).0, 0x00000000_FFFFFFFF);
932
933 let all_ones = Word(0xFFFFFFFF_FFFFFFFF);
935 assert_eq!(all_ones.sll32(1).0, 0xFFFFFFFE_FFFFFFFE);
936 assert_eq!(all_ones.srl32(1).0, 0x7FFFFFFF_7FFFFFFF);
937 assert_eq!(all_ones.sra32(1).0, 0xFFFFFFFF_FFFFFFFF);
938
939 let alternating = Word(0xAAAAAAAA_55555555);
940 assert_eq!(alternating.sll32(1).0, 0x55555554_AAAAAAAA);
941 assert_eq!(alternating.srl32(1).0, 0x55555555_2AAAAAAA);
942 assert_eq!(alternating.sra32(1).0, 0xD5555555_2AAAAAAA);
943
944 assert_eq!(w1.sll32(0), w1);
946 assert_eq!(w1.srl32(0), w1);
947 assert_eq!(w1.sra32(0), w1);
948
949 let w6 = Word(0x00000001_00000000);
951 assert_eq!(w6.sll32(31).0, 0x80000000_00000000);
952 assert_eq!(w6.srl32(1).0, 0x00000000_00000000);
953
954 let w7 = Word(0x80000001_80000001);
956 assert_eq!(w7.rotr32(1).0, 0xC0000000_C0000000);
957 assert_eq!(w7.rotr32(31).0, 0x00000003_00000003);
958
959 let w8 = Word(0x12345678_9ABCDEF0);
961 assert_eq!(w8.rotr32(4).0, 0x81234567_09ABCDEF);
962 assert_eq!(w8.rotr32(16).0, 0x56781234_DEF09ABC);
963
964 let w9 = Word(0xFFFF0000_0000FFFF);
966 assert_eq!(w9.rotr32(16).0, 0x0000FFFF_FFFF0000);
967
968 assert_eq!(w8.rotr32(0), w8);
970 }
971}