1use std::{
5 fmt,
6 ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr},
7};
8
9use binius_utils::{
10 checked_arithmetics::checked_log_2,
11 serialization::{DeserializeBytes, SerializationError, SerializeBytes},
12};
13use bytemuck::{Pod, Zeroable};
14use bytes::{Buf, BufMut};
15
16#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Pod, Zeroable)]
22#[repr(transparent)]
23pub struct Word(pub u64);
24
25impl Word {
26 pub const BYTES: usize = size_of::<Word>();
28 pub const LOG_BYTES: usize = checked_log_2(Self::BYTES);
30 pub const BITS: usize = Self::BYTES * 8;
32 pub const LOG_BITS: usize = checked_log_2(Self::BITS);
34
35 pub const ZERO: Word = Word(0);
37 pub const ONE: Word = Word(1);
39 pub const ALL_ONE: Word = Word(u64::MAX);
41 pub const MASK_32: Word = Word(0x00000000FFFFFFFF);
43 pub const MSB_ONE: Word = Word(0x8000000000000000);
47
48 pub const fn from_u64(value: u64) -> Word {
50 Word(value)
51 }
52
53 pub const fn extract_bit(self, i: usize) -> bool {
57 (self.0 >> i) & 1 == 1
58 }
59
60 pub const fn iadd32_cin_cout(self, rhs: Word, cin: Word) -> (Word, Word) {
69 let Word(lhs) = self;
70 let Word(rhs) = rhs;
71 let Word(cin) = cin;
72
73 let cin_lo = (cin >> 31) & 1;
75 let cin_hi = (cin >> 63) & 1;
76
77 let lo_l = lhs as u32;
79 let hi_l = (lhs >> 32) as u32;
80 let lo_r = rhs as u32;
81 let hi_r = (rhs >> 32) as u32;
82
83 let lo_sum = (lo_l as u64) + (lo_r as u64) + cin_lo;
85 let hi_sum = (hi_l as u64) + (hi_r as u64) + cin_hi;
86 let sum = (lo_sum as u32 as u64) | ((hi_sum as u32 as u64) << 32);
87
88 let cout = (lhs & rhs) | ((lhs ^ rhs) & !sum);
89 (Word(sum), Word(cout))
90 }
91
92 pub const fn iadd_cout_32(self, rhs: Word) -> (Word, Word) {
96 self.iadd32_cin_cout(rhs, Word::ZERO)
97 }
98
99 pub fn iadd_cin_cout(self, rhs: Word, cin: Word) -> (Word, Word) {
107 debug_assert!(cin == Word::ZERO || cin == Word::ONE, "cin must be 0 or 1");
108 let Word(lhs) = self;
109 let Word(rhs) = rhs;
110 let Word(cin) = cin;
111 let sum = lhs.wrapping_add(rhs).wrapping_add(cin);
112 let cout = (lhs & rhs) | ((lhs ^ rhs) & !sum);
113 (Word(sum), Word(cout))
114 }
115
116 pub fn isub_bin_bout(self, rhs: Word, bin: Word) -> (Word, Word) {
124 debug_assert!(bin == Word::ZERO || bin == Word::ONE, "bin must be 0 or 1");
125 let Word(lhs) = self;
126 let Word(rhs) = rhs;
127 let Word(bin) = bin;
128 let diff = lhs.wrapping_sub(rhs).wrapping_sub(bin);
129 let bout = (!lhs & rhs) | (!(lhs ^ rhs) & diff);
130 (Word(diff), Word(bout))
131 }
132
133 pub const fn shr_32(self, n: u32) -> Word {
135 let Word(value) = self;
136 let result = (value >> n) & Self::MASK_32.0;
138 Word(result)
139 }
140
141 pub const fn sar(self, n: u32) -> Word {
145 let Word(value) = self;
146 let value = value as i64;
147 let result = value >> n;
148 Word(result as u64)
149 }
150
151 pub const fn rotr(self, n: u32) -> Word {
153 let Word(value) = self;
154 Word(value.rotate_right(n))
155 }
156
157 pub const fn sll32(self, n: u32) -> Word {
162 let Word(value) = self;
163 let n = n & 0x1F; let lo = value as u32;
167 let hi = (value >> 32) as u32;
168
169 let lo_shifted = (lo << n) as u64;
171 let hi_shifted = ((hi << n) as u64) << 32;
172
173 Word(lo_shifted | hi_shifted)
174 }
175
176 pub const fn srl32(self, n: u32) -> Word {
181 let Word(value) = self;
182 let n = n & 0x1F; let lo = value as u32;
186 let hi = (value >> 32) as u32;
187
188 let lo_shifted = (lo >> n) as u64;
190 let hi_shifted = ((hi >> n) as u64) << 32;
191
192 Word(lo_shifted | hi_shifted)
193 }
194
195 pub const fn sra32(self, n: u32) -> Word {
201 let Word(value) = self;
202 let n = n & 0x1F; let lo = value as u32 as i32;
206 let hi = (value >> 32) as u32 as i32;
207
208 let lo_shifted = ((lo >> n) as u32) as u64;
210 let hi_shifted = (((hi >> n) as u32) as u64) << 32;
211
212 Word(lo_shifted | hi_shifted)
213 }
214
215 pub const fn rotr32(self, n: u32) -> Word {
221 let Word(value) = self;
222 let n = n & 0x1F; let lo = value as u32;
226 let hi = (value >> 32) as u32;
227
228 let lo_rotated = lo.rotate_right(n) as u64;
230 let hi_rotated = (hi.rotate_right(n) as u64) << 32;
231
232 Word(lo_rotated | hi_rotated)
233 }
234
235 pub const fn imul(self, rhs: Word) -> (Word, Word) {
240 let Word(lhs) = self;
241 let Word(rhs) = rhs;
242 let result = (lhs as u128) * (rhs as u128);
243
244 let hi = (result >> 64) as u64;
245 let lo = result as u64;
246 (Word(hi), Word(lo))
247 }
248
249 pub const fn smul(self, rhs: Word) -> (Word, Word) {
254 let Word(lhs) = self;
255 let Word(rhs) = rhs;
256 let a = lhs as i64;
258 let b = rhs as i64;
259 let result = (a as i128) * (b as i128);
261 let hi = (result >> 64) as u64;
263 let lo = result as u64;
264 (Word(hi), Word(lo))
265 }
266
267 pub const fn wrapping_add(self, rhs: Word) -> Word {
271 Word(self.0.wrapping_add(rhs.0))
272 }
273
274 pub const fn wrapping_sub(self, rhs: Word) -> Word {
278 Word(self.0.wrapping_sub(rhs.0))
279 }
280
281 pub const fn as_u64(self) -> u64 {
283 self.0
284 }
285
286 pub const fn is_msb_true(self) -> bool {
293 (self.0 & Self::MSB_ONE.0) != 0
294 }
295
296 pub const fn is_msb_false(self) -> bool {
303 !self.is_msb_true()
304 }
305}
306
307impl fmt::Debug for Word {
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 write!(f, "Word({:#018x})", self.0)
310 }
311}
312
313impl BitAnd for Word {
314 type Output = Self;
315
316 fn bitand(self, rhs: Self) -> Self::Output {
317 Word(self.0 & rhs.0)
318 }
319}
320
321impl BitOr for Word {
322 type Output = Self;
323
324 fn bitor(self, rhs: Self) -> Self::Output {
325 Word(self.0 | rhs.0)
326 }
327}
328
329impl BitXor for Word {
330 type Output = Self;
331
332 fn bitxor(self, rhs: Self) -> Self::Output {
333 Word(self.0 ^ rhs.0)
334 }
335}
336
337impl Shl<u32> for Word {
338 type Output = Self;
339
340 fn shl(self, rhs: u32) -> Self::Output {
341 Word(self.0 << rhs)
342 }
343}
344
345impl Shr<u32> for Word {
346 type Output = Self;
347
348 fn shr(self, rhs: u32) -> Self::Output {
349 Word(self.0 >> rhs)
350 }
351}
352
353impl Not for Word {
354 type Output = Self;
355
356 fn not(self) -> Self::Output {
357 Word(!self.0)
358 }
359}
360
361impl SerializeBytes for Word {
362 fn serialize(&self, write_buf: impl BufMut) -> Result<(), SerializationError> {
363 self.0.serialize(write_buf)
364 }
365}
366
367impl DeserializeBytes for Word {
368 fn deserialize(read_buf: impl Buf) -> Result<Self, SerializationError>
369 where
370 Self: Sized,
371 {
372 Ok(Word(u64::deserialize(read_buf)?))
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use proptest::prelude::*;
379 use rand::{Rng, SeedableRng, rngs::StdRng};
380
381 use super::*;
382
383 #[test]
384 fn test_constants() {
385 assert_eq!(Word::ZERO, Word(0));
386 assert_eq!(Word::ONE, Word(1));
387 assert_eq!(Word::ALL_ONE, Word(0xFFFFFFFFFFFFFFFF));
388 assert_eq!(Word::MASK_32, Word(0x00000000FFFFFFFF));
389 assert_eq!(Word::MSB_ONE, Word(0x8000000000000000));
390 }
391
392 #[test]
393 fn test_msb_bool() {
394 assert!(Word::MSB_ONE.is_msb_true());
396 assert!(!Word::MSB_ONE.is_msb_false());
397
398 assert!(!Word::ZERO.is_msb_true());
400 assert!(Word::ZERO.is_msb_false());
401
402 assert!(Word(0x8000000000000000).is_msb_true());
404 assert!(Word(0x8000000000000001).is_msb_true());
405 assert!(Word(0x80000000FFFFFFFF).is_msb_true());
406 assert!(Word(0xFFFFFFFFFFFFFFFF).is_msb_true());
407
408 assert!(Word(0x7FFFFFFFFFFFFFFF).is_msb_false());
410 assert!(Word(0x0000000000000001).is_msb_false());
411 assert!(Word(0x00000000FFFFFFFF).is_msb_false());
412 assert!(Word(0x7000000000000000).is_msb_false());
413
414 let test_word = Word(0x8123456789ABCDEF);
416 assert!(test_word.is_msb_true());
417 assert!(!test_word.is_msb_false());
418
419 let test_word2 = Word(0x7123456789ABCDEF);
420 assert!(!test_word2.is_msb_true());
421 assert!(test_word2.is_msb_false());
422 }
423
424 proptest! {
425 #[test]
426 fn prop_msb_bool(val in any::<u64>()) {
427 let word = Word(val);
428
429 assert_eq!(word.is_msb_true(), !word.is_msb_false());
431 assert_eq!(word.is_msb_false(), !word.is_msb_true());
432
433 let msb_set = (val & 0x8000000000000000) != 0;
435 assert_eq!(word.is_msb_true(), msb_set);
436 assert_eq!(word.is_msb_false(), !msb_set);
437
438 let word_with_msb = Word(val | 0x8000000000000000);
440 let word_without_msb = Word(val & 0x7FFFFFFFFFFFFFFF);
441 assert!(word_with_msb.is_msb_true());
442 assert!(word_without_msb.is_msb_false());
443 }
444
445 #[test]
446 fn prop_bitwise_and(a in any::<u64>(), b in any::<u64>()) {
447 let wa = Word(a);
448 let wb = Word(b);
449
450 assert_eq!((wa & wb).0, a & b);
452 assert_eq!(wa & Word::ALL_ONE, wa);
453 assert_eq!(wa & Word::ZERO, Word::ZERO);
454 assert_eq!(wa & wa, wa); assert_eq!(wa & wb, wb & wa);
458 }
459
460 #[test]
461 fn prop_bitwise_or(a in any::<u64>(), b in any::<u64>()) {
462 let wa = Word(a);
463 let wb = Word(b);
464
465 assert_eq!((wa | wb).0, a | b);
467 assert_eq!(wa | Word::ZERO, wa);
468 assert_eq!(wa | Word::ALL_ONE, Word::ALL_ONE);
469 assert_eq!(wa | wa, wa); assert_eq!(wa | wb, wb | wa);
473 }
474
475 #[test]
476 fn prop_bitwise_xor(a in any::<u64>(), b in any::<u64>()) {
477 let wa = Word(a);
478 let wb = Word(b);
479
480 assert_eq!((wa ^ wb).0, a ^ b);
482 assert_eq!(wa ^ Word::ZERO, wa);
483 assert_eq!(wa ^ wa, Word::ZERO);
484 assert_eq!(wa ^ Word::ALL_ONE, !wa);
485
486 assert_eq!(wa ^ wb, wb ^ wa);
488
489 assert_eq!(wa ^ wb ^ wb, wa);
491 }
492
493 #[test]
494 fn prop_bitwise_not(a in any::<u64>()) {
495 let wa = Word(a);
496
497 assert_eq!((!wa).0, !a);
499 assert_eq!(!(!wa), wa); assert_eq!(!Word::ZERO, Word::ALL_ONE);
501 assert_eq!(!Word::ALL_ONE, Word::ZERO);
502
503 let wb = Word(a.wrapping_add(1));
505 assert_eq!(!(wa & wb), !wa | !wb);
506 assert_eq!(!(wa | wb), !wa & !wb);
507 }
508
509 #[test]
510 fn prop_shift_left(val in any::<u64>(), shift in 0u32..64) {
511 let w = Word(val);
512 assert_eq!((w << shift).0, val << shift);
513
514 assert_eq!(w << 0, w);
516
517 if shift >= 64 {
519 assert_eq!((w << shift).0, 0);
520 }
521 }
522
523 #[test]
524 fn prop_shift_right(val in any::<u64>(), shift in 0u32..64) {
525 let w = Word(val);
526 assert_eq!((w >> shift).0, val >> shift);
527
528 assert_eq!(w >> 0, w);
530
531 if shift >= 64 {
533 assert_eq!((w >> shift).0, 0);
534 }
535 }
536
537 #[test]
538 fn prop_shift_inverse(val in any::<u64>(), shift in 1u32..64) {
539 let w = Word(val);
540 let mask = (1u64 << (64 - shift)) - 1;
542 assert_eq!(((w << shift) >> shift).0, val & mask);
543
544 let high_mask = !((1u64 << shift) - 1);
546 assert_eq!(((w >> shift) << shift).0, val & high_mask);
547 }
548
549 #[test]
550 fn prop_sar(val in any::<u64>(), shift in 0u32..64) {
551 let w = Word(val);
552 let expected = ((val as i64) >> shift) as u64;
553 assert_eq!(w.sar(shift).0, expected);
554
555 assert_eq!(w.sar(0), w);
557
558 let sign_extended = if (val as i64) < 0 {
560 Word(0xFFFFFFFFFFFFFFFF)
561 } else {
562 Word(0)
563 };
564 assert_eq!(w.sar(63), sign_extended);
565 }
566
567 #[test]
568 fn prop_sar_sign_extension(val in any::<u64>(), shift in 1u32..64) {
569 let w = Word(val);
570 let result = w.sar(shift);
571
572 let is_negative = (val as i64) < 0;
574 if is_negative {
575 let mask = !((1u64 << (64 - shift)) - 1);
577 assert_eq!(result.0 & mask, mask);
578 } else {
579 let mask = !((1u64 << (64 - shift)) - 1);
581 assert_eq!(result.0 & mask, 0);
582 }
583 }
584
585 #[test]
586 fn prop_iadd32_cin_cout(
587 a in any::<u64>(), b in any::<u64>(),
588 cin_lo in proptest::bool::ANY, cin_hi in proptest::bool::ANY,
589 ) {
590 let cin_word = ((cin_lo as u64) << 31) | ((cin_hi as u64) << 63);
592 let wa = Word(a);
593 let wb = Word(b);
594 let wcin = Word(cin_word);
595 let (sum, cout) = wa.iadd32_cin_cout(wb, wcin);
596
597 let lo_sum = (a as u32 as u64) + (b as u32 as u64) + (cin_lo as u64);
599 let hi_sum = ((a >> 32) as u32 as u64) + ((b >> 32) as u32 as u64) + (cin_hi as u64);
600 let expected_sum = (lo_sum as u32 as u64) | ((hi_sum as u32 as u64) << 32);
601 assert_eq!(sum.0, expected_sum);
602
603 let expected_cout = (a & b) | ((a ^ b) & !expected_sum);
605 assert_eq!(cout.0, expected_cout);
606
607 let (sum0, cout0) = wa.iadd_cout_32(wb);
609 let (sum1, cout1) = wa.iadd32_cin_cout(wb, Word::ZERO);
610 assert_eq!(sum0, sum1);
611 assert_eq!(cout0, cout1);
612 }
613
614 #[test]
615 fn prop_iadd_cin_cout(a in any::<u64>(), b in any::<u64>(), cin in 0u64..=1) {
616 let wa = Word(a);
617 let wb = Word(b);
618 let wcin = Word(cin);
619 let (sum, cout) = wa.iadd_cin_cout(wb, wcin);
620
621 let expected_sum = a.wrapping_add(b).wrapping_add(cin);
623 assert_eq!(sum.0, expected_sum);
624
625 let expected_cout = (a & b) | ((a ^ b) & !expected_sum);
627 assert_eq!(cout.0, expected_cout);
628
629 let (sum0, cout0) = wa.iadd_cin_cout(wb, Word::ZERO);
631 let full_sum = a.wrapping_add(b);
632 assert_eq!(sum0.0, full_sum);
633 assert_eq!(cout0.0, (a & b) | ((a ^ b) & !full_sum));
634 }
635
636 #[test]
637 fn prop_isub_bin_bout(a in any::<u64>(), b in any::<u64>(), bin in 0u64..=1) {
638 let wa = Word(a);
639 let wb = Word(b);
640 let wbin = Word(bin);
641 let (diff, bout) = wa.isub_bin_bout(wb, wbin);
642
643 let expected_diff = a.wrapping_sub(b).wrapping_sub(bin);
645 assert_eq!(diff.0, expected_diff);
646
647 let expected_bout = (!a & b) | (!(a ^ b) & expected_diff);
649 assert_eq!(bout.0, expected_bout);
650
651 let (diff0, bout0) = wa.isub_bin_bout(wb, Word::ZERO);
653 let expected = a.wrapping_sub(b);
654 assert_eq!(diff0.0, expected);
655 assert_eq!(bout0.0, (!a & b) | (!(a ^ b) & expected));
656 }
657
658 #[test]
659 fn prop_shr_32(val in any::<u64>(), shift in 0u32..64) {
660 let w = Word(val);
661 let result = w.shr_32(shift);
662
663 let expected = (val >> shift) & 0xFFFFFFFF;
665 assert_eq!(result.0, expected);
666
667 assert_eq!(w.shr_32(0).0, val & 0xFFFFFFFF);
669
670 if shift >= 32 {
672 assert_eq!(result.0, (val >> shift) & 0xFFFFFFFF);
673 }
674 }
675 #[test]
676 fn prop_rotr(val in any::<u64>(), rotate in 0u32..128) {
677 let w = Word(val);
678 let result = w.rotr(rotate);
679
680 let rotate_mod = rotate % 64;
682 let expected = val.rotate_right(rotate_mod);
683 assert_eq!(result.0, expected);
684
685 assert_eq!(w.rotr(0), w);
687 assert_eq!(w.rotr(64), w);
688
689 let r1 = rotate % 64;
691 let r2 = (64 - r1) % 64;
692 if r1 != 0 {
693 assert_eq!(w.rotr(r1).rotr(r2), w);
694 }
695 }
696
697 #[test]
698 fn prop_imul(a in any::<u64>(), b in any::<u64>()) {
699 let wa = Word(a);
700 let wb = Word(b);
701 let (hi, lo) = wa.imul(wb);
702
703 let result = (a as u128) * (b as u128);
705 assert_eq!(hi.0, (result >> 64) as u64);
706 assert_eq!(lo.0, result as u64);
707
708 let (hi0, lo0) = wa.imul(Word::ZERO);
710 assert_eq!(hi0, Word::ZERO);
711 assert_eq!(lo0, Word::ZERO);
712
713 let (hi1, lo1) = wa.imul(Word::ONE);
715 assert_eq!(hi1, Word::ZERO);
716 assert_eq!(lo1, wa);
717
718 let (hi_ab, lo_ab) = wa.imul(wb);
720 let (hi_reversed, lo_reversed) = wb.imul(wa);
721 assert_eq!(hi_ab, hi_reversed);
722 assert_eq!(lo_ab, lo_reversed);
723 }
724
725 #[test]
726 fn prop_sll32(val in any::<u64>(), shift in 0u32..32) {
727 let w = Word(val);
728 let result = w.sll32(shift);
729
730 let lo = val as u32;
732 let hi = (val >> 32) as u32;
733
734 let expected_lo = ((lo << shift) as u64) & 0xFFFFFFFF;
736 let expected_hi = ((hi << shift) as u64) << 32;
737 let expected = expected_lo | expected_hi;
738
739 assert_eq!(result.0, expected);
740
741 assert_eq!(w.sll32(0), w);
743
744 let w_test = Word(0x40000001_40000001);
746 let result_31 = w_test.sll32(31);
747 assert_eq!(result_31.0, 0x80000000_80000000);
748
749 assert_eq!(w.sll32(shift), w.sll32(shift | 0x20));
751 }
752
753 #[test]
754 fn prop_srl32(val in any::<u64>(), shift in 0u32..32) {
755 let w = Word(val);
756 let result = w.srl32(shift);
757
758 let lo = val as u32;
760 let hi = (val >> 32) as u32;
761
762 let expected_lo = (lo >> shift) as u64;
764 let expected_hi = ((hi >> shift) as u64) << 32;
765 let expected = expected_lo | expected_hi;
766
767 assert_eq!(result.0, expected);
768
769 assert_eq!(w.srl32(0), w);
771
772 let w_test = Word(0x80000000_80000000);
774 let result_31 = w_test.srl32(31);
775 assert_eq!(result_31.0, 0x00000001_00000001);
776
777 assert_eq!(w.srl32(shift), w.srl32(shift | 0x20));
779 }
780
781 #[test]
782 fn prop_sra32(val in any::<u64>(), shift in 0u32..32) {
783 let w = Word(val);
784 let result = w.sra32(shift);
785
786 let lo = val as u32 as i32;
788 let hi = (val >> 32) as u32 as i32;
789
790 let expected_lo = ((lo >> shift) as u32) as u64;
792 let expected_hi = (((hi >> shift) as u32) as u64) << 32;
793 let expected = expected_lo | expected_hi;
794
795 assert_eq!(result.0, expected);
796
797 assert_eq!(w.sra32(0), w);
799
800 let w_neg = Word(0x80000000_80000000);
802 let result_1 = w_neg.sra32(1);
803 assert_eq!(result_1.0, 0xC0000000_C0000000);
804
805 let w_pos = Word(0x40000000_40000000);
807 let result_1_pos = w_pos.sra32(1);
808 assert_eq!(result_1_pos.0, 0x20000000_20000000);
809
810 let result_31 = w.sra32(31);
812 let expected_lo_31 = if lo < 0 { 0xFFFFFFFF } else { 0 };
813 let expected_hi_31 = if hi < 0 { 0xFFFFFFFF00000000 } else { 0 };
814 assert_eq!(result_31.0, expected_lo_31 | expected_hi_31);
815
816 assert_eq!(w.sra32(shift), w.sra32(shift | 0x20));
818 }
819
820 #[test]
821 fn prop_rotr32(val in any::<u64>(), rotate in 0u32..32) {
822 let w = Word(val);
823 let result = w.rotr32(rotate);
824
825 let lo = val as u32;
827 let hi = (val >> 32) as u32;
828
829 let expected_lo = lo.rotate_right(rotate) as u64;
831 let expected_hi = ((hi.rotate_right(rotate)) as u64) << 32;
832 let expected = expected_lo | expected_hi;
833
834 assert_eq!(result.0, expected);
835
836 assert_eq!(w.rotr32(0), w);
838
839 assert_eq!(w.rotr32(32), w.rotr32(0));
841
842 assert_eq!(w.rotr32(rotate), w.rotr32(rotate | 0x20));
844
845 if rotate > 0 && rotate < 32 {
847 let w_test = Word(0x12345678_9ABCDEF0);
848 let rotated = w_test.rotr32(rotate);
849 let back = rotated.rotr32(32 - rotate);
850 assert_eq!(back, w_test);
851 }
852 }
853
854 #[test]
855 fn prop_smul(a in any::<u64>(), b in any::<u64>()) {
856 let wa = Word(a);
857 let wb = Word(b);
858 let (hi, lo) = wa.smul(wb);
859
860 let result = (a as i64 as i128) * (b as i64 as i128);
862 assert_eq!(hi.0, (result >> 64) as u64);
863 assert_eq!(lo.0, result as u64);
864
865 let (hi0, lo0) = wa.smul(Word::ZERO);
867 assert_eq!(hi0, Word::ZERO);
868 assert_eq!(lo0, Word::ZERO);
869
870 let (hi1, lo1) = wa.smul(Word::ONE);
872 let expected_hi = if (a as i64) < 0 { Word(0xFFFFFFFFFFFFFFFF) } else { Word::ZERO };
873 assert_eq!(hi1, expected_hi);
874 assert_eq!(lo1, wa);
875
876 let (hi_neg, lo_neg) = wa.smul(Word(0xFFFFFFFFFFFFFFFF));
878 let neg_result = -(a as i64 as i128);
879 assert_eq!(hi_neg.0, (neg_result >> 64) as u64);
880 assert_eq!(lo_neg.0, neg_result as u64);
881
882 let (hi_ab, lo_ab) = wa.smul(wb);
884 let (hi_reversed, lo_reversed) = wb.smul(wa);
885 assert_eq!(hi_ab, hi_reversed);
886 assert_eq!(lo_ab, lo_reversed);
887 }
888
889 #[test]
890 fn prop_wrapping_sub(a in any::<u64>(), b in any::<u64>()) {
891 let wa = Word(a);
892 let wb = Word(b);
893 let result = wa.wrapping_sub(wb);
894
895 assert_eq!(result.0, a.wrapping_sub(b));
896
897 assert_eq!(wa.wrapping_sub(Word::ZERO), wa);
899
900 assert_eq!(wa.wrapping_sub(wa), Word::ZERO);
902
903 let sum = Word(a.wrapping_add(b));
905 assert_eq!(sum.wrapping_sub(wb), wa);
906 }
907
908 #[test]
909 fn prop_conversions(val in any::<u64>()) {
910 let word = Word::from_u64(val);
911 assert_eq!(word.as_u64(), val);
912 assert_eq!(word, Word(val));
913
914 assert_eq!(Word::from_u64(word.as_u64()), word);
916 }
917
918 #[test]
919 fn prop_debug_format(val in any::<u64>()) {
920 let word = Word(val);
921 let debug_str = format!("{:?}", word);
922 assert!(debug_str.starts_with("Word(0x"));
923 assert!(debug_str.ends_with(")"));
924 let expected = format!("Word({:#018x})", val);
926 assert_eq!(debug_str, expected);
927 }
928 }
929
930 #[test]
931 fn test_32bit_shift_edge_cases() {
932 let w1 = Word(0x12345678_9ABCDEF0);
934 assert_eq!(w1.sll32(4).0, 0x23456780_ABCDEF00);
935 assert_eq!(w1.sll32(16).0, 0x56780000_DEF00000);
936
937 let w2 = Word(0xFFFFFFFF_00000000);
939 assert_eq!(w2.sll32(1).0, 0xFFFFFFFE_00000000);
940 let w3 = Word(0x00000000_FFFFFFFF);
941 assert_eq!(w3.sll32(1).0, 0x00000000_FFFFFFFE);
942
943 assert_eq!(w1.srl32(4).0, 0x01234567_09ABCDEF);
945 assert_eq!(w1.srl32(16).0, 0x00001234_00009ABC);
946
947 let w4 = Word(0x80000000_7FFFFFFF); assert_eq!(w4.sra32(1).0, 0xC0000000_3FFFFFFF);
950 assert_eq!(w4.sra32(31).0, 0xFFFFFFFF_00000000);
951
952 let w5 = Word(0x7FFFFFFF_80000000); assert_eq!(w5.sra32(1).0, 0x3FFFFFFF_C0000000);
954 assert_eq!(w5.sra32(31).0, 0x00000000_FFFFFFFF);
955
956 let all_ones = Word(0xFFFFFFFF_FFFFFFFF);
958 assert_eq!(all_ones.sll32(1).0, 0xFFFFFFFE_FFFFFFFE);
959 assert_eq!(all_ones.srl32(1).0, 0x7FFFFFFF_7FFFFFFF);
960 assert_eq!(all_ones.sra32(1).0, 0xFFFFFFFF_FFFFFFFF);
961
962 let alternating = Word(0xAAAAAAAA_55555555);
963 assert_eq!(alternating.sll32(1).0, 0x55555554_AAAAAAAA);
964 assert_eq!(alternating.srl32(1).0, 0x55555555_2AAAAAAA);
965 assert_eq!(alternating.sra32(1).0, 0xD5555555_2AAAAAAA);
966
967 assert_eq!(w1.sll32(0), w1);
969 assert_eq!(w1.srl32(0), w1);
970 assert_eq!(w1.sra32(0), w1);
971
972 let w6 = Word(0x00000001_00000000);
974 assert_eq!(w6.sll32(31).0, 0x80000000_00000000);
975 assert_eq!(w6.srl32(1).0, 0x00000000_00000000);
976
977 let w7 = Word(0x80000001_80000001);
979 assert_eq!(w7.rotr32(1).0, 0xC0000000_C0000000);
980 assert_eq!(w7.rotr32(31).0, 0x00000003_00000003);
981
982 let w8 = Word(0x12345678_9ABCDEF0);
984 assert_eq!(w8.rotr32(4).0, 0x81234567_09ABCDEF);
985 assert_eq!(w8.rotr32(16).0, 0x56781234_DEF09ABC);
986
987 let w9 = Word(0xFFFF0000_0000FFFF);
989 assert_eq!(w9.rotr32(16).0, 0x0000FFFF_FFFF0000);
990
991 assert_eq!(w8.rotr32(0), w8);
993 }
994
995 #[test]
996 fn test_word_serialization_round_trip() {
997 let mut rng = StdRng::seed_from_u64(0);
998 let word = Word::from_u64(rng.next_u64());
999
1000 let mut buf = Vec::new();
1001 word.serialize(&mut buf).unwrap();
1002
1003 let deserialized = Word::deserialize(&mut buf.as_slice()).unwrap();
1004 assert_eq!(word, deserialized);
1005 }
1006}