1use core::fmt::{self, Debug, Formatter};
4use core::iter::{Product, Sum};
5use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
6use core::slice;
7
8use p3_field::{Algebra, Field, PrimeCharacteristicRing};
9use rand::Rng;
10use rand::distr::{Distribution, StandardUniform};
11
12use super::underlier::{Divisible, M128, M256, M512, Underlier, Word};
13use crate::Gf2;
14use crate::gf2::characteristic_two_methods;
15
16const BLOCK_MASKS: [u64; 6] = [
28 0x5555_5555_5555_5555,
29 0x3333_3333_3333_3333,
30 0x0f0f_0f0f_0f0f_0f0f,
31 0x00ff_00ff_00ff_00ff,
32 0x0000_ffff_0000_ffff,
33 0x0000_0000_ffff_ffff,
34];
35
36#[inline]
44pub(super) const fn block_mask(block: usize) -> u64 {
45 assert!(block.is_power_of_two() && block <= 32, "block out of range");
47 BLOCK_MASKS[block.trailing_zeros() as usize]
48}
49
50#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
65#[repr(transparent)]
66#[must_use]
67pub struct PackedGf2<U: Underlier>(U);
68
69pub type PackedGf2x8 = PackedGf2<u8>;
71
72pub type PackedGf2x16 = PackedGf2<u16>;
74
75pub type PackedGf2x32 = PackedGf2<u32>;
77
78pub type PackedGf2x64 = PackedGf2<u64>;
80
81pub type PackedGf2x128 = PackedGf2<M128>;
83
84pub type PackedGf2x256 = PackedGf2<M256>;
86
87pub type PackedGf2x512 = PackedGf2<M512>;
89
90impl<U: Underlier> PackedGf2<U> {
91 pub const WIDTH: usize = U::BITS;
93
94 pub(super) const WORD_BITS: usize = <U::Word as Word>::BITS;
96
97 #[inline]
99 pub const fn new(bits: U) -> Self {
100 Self(bits)
101 }
102
103 #[inline]
105 pub const fn to_bits(self) -> U {
106 self.0
107 }
108
109 #[inline]
111 #[must_use]
112 pub fn words(&self) -> &[U::Word] {
113 self.0.words()
114 }
115
116 #[inline]
120 pub fn words_mut(&mut self) -> &mut [U::Word] {
121 self.0.words_mut()
122 }
123
124 #[inline]
129 pub fn get(&self, lane: usize) -> Gf2 {
130 assert!(lane < Self::WIDTH, "lane index out of range");
131
132 let word = self.0.words()[lane / Self::WORD_BITS];
134 let bit = (word >> (lane % Self::WORD_BITS)) & U::Word::from_u64(1);
135 Gf2::new(bit.to_u64() as u8)
136 }
137
138 #[inline]
143 pub fn set(&mut self, lane: usize, value: Gf2) {
144 assert!(lane < Self::WIDTH, "lane index out of range");
145 let index = lane / Self::WORD_BITS;
146 let shift = lane % Self::WORD_BITS;
147
148 let word = self.0.words()[index];
151 let cleared = word & !(U::Word::from_u64(1) << shift);
152 let bit = U::Word::from_u64(u64::from(value.to_bit())) << shift;
153 self.0.words_mut()[index] = cleared | bit;
154 }
155
156 #[inline]
158 pub fn from_fn(mut f: impl FnMut(usize) -> Gf2) -> Self {
159 let mut out = Self(U::ZERO);
160
161 for lane in 0..Self::WIDTH {
163 out.set(lane, f(lane));
164 }
165 out
166 }
167
168 #[inline]
170 pub fn broadcast(value: Gf2) -> Self {
171 let word = U::Word::from_u64(0u64.wrapping_sub(u64::from(value.to_bit())));
174 Self(U::from_words_fn(|_| word))
175 }
176
177 #[inline]
179 pub fn lanes(self) -> impl Iterator<Item = Gf2> {
180 (0..Self::WIDTH).map(move |lane| self.get(lane))
181 }
182
183 #[inline]
185 #[must_use]
186 pub fn count_ones(&self) -> u32 {
187 self.0.words().iter().map(|word| word.count_ones()).sum()
189 }
190
191 #[inline]
197 pub const fn invert_or_zero(self) -> Self {
198 self
199 }
200
201 #[inline]
205 pub fn narrow<V: Underlier>(&self) -> &[PackedGf2<V>]
206 where
207 U: Divisible<V>,
208 {
209 wrap(self.0.parts())
210 }
211
212 #[inline]
214 pub fn narrow_mut<V: Underlier>(&mut self) -> &mut [PackedGf2<V>]
215 where
216 U: Divisible<V>,
217 {
218 wrap_mut(self.0.parts_mut())
219 }
220
221 #[inline]
225 pub fn narrow_slice<V: Underlier>(slice: &[Self]) -> &[PackedGf2<V>]
226 where
227 U: Divisible<V>,
228 {
229 wrap(<U as Divisible<V>>::split_slice(unwrap(slice)))
230 }
231
232 #[inline]
234 pub fn narrow_slice_mut<V: Underlier>(slice: &mut [Self]) -> &mut [PackedGf2<V>]
235 where
236 U: Divisible<V>,
237 {
238 wrap_mut(<U as Divisible<V>>::split_slice_mut(unwrap_mut(slice)))
239 }
240
241 #[inline]
245 #[must_use]
246 pub const fn as_bytes(slice: &[Self]) -> &[u8] {
247 const {
252 assert!(size_of::<Self>() * 8 == U::BITS);
253 assert!(
254 cfg!(target_endian = "little"),
255 "the byte view of a packing needs a little-endian target"
256 );
257 }
258 let len = slice.len() * (U::BITS / 8);
259
260 unsafe { slice::from_raw_parts(slice.as_ptr().cast::<u8>(), len) }
263 }
264
265 pub fn interleave(&self, other: Self, block_len: usize) -> (Self, Self) {
284 assert!(
285 block_len.is_power_of_two() && block_len <= Self::WIDTH,
286 "block length must be a power of two dividing the width"
287 );
288
289 if block_len == Self::WIDTH {
291 return (*self, other);
292 }
293 (
294 self.interleave_from(other, block_len, 0),
295 self.interleave_from(other, block_len, 1),
296 )
297 }
298
299 fn interleave_from(&self, other: Self, block_len: usize, take: usize) -> Self {
304 let bits = Self::WORD_BITS;
305 let (left, right) = (self.0.words(), other.0.words());
306
307 Self(U::from_words_fn(|w| {
308 let first = w * bits;
310
311 if block_len >= bits {
312 let chunk = first / block_len;
314 let source_lane = (2 * (chunk / 2) + take) * block_len + first % block_len;
315 let source = if chunk.is_multiple_of(2) { left } else { right };
316 source[source_lane / bits]
317 } else {
318 let mask = U::Word::from_u64(block_mask(block_len));
321 let x = (left[w] >> (take * block_len)) & mask;
322 let y = (right[w] >> (take * block_len)) & mask;
323 x | (y << block_len)
324 }
325 }))
326 }
327}
328
329#[inline]
333const fn wrap<V: Underlier>(parts: &[V]) -> &[PackedGf2<V>] {
334 unsafe { slice::from_raw_parts(parts.as_ptr().cast(), parts.len()) }
336}
337
338#[inline]
340const fn wrap_mut<V: Underlier>(parts: &mut [V]) -> &mut [PackedGf2<V>] {
341 unsafe { slice::from_raw_parts_mut(parts.as_mut_ptr().cast(), parts.len()) }
343}
344
345#[inline]
347const fn unwrap<V: Underlier>(packings: &[PackedGf2<V>]) -> &[V] {
348 unsafe { slice::from_raw_parts(packings.as_ptr().cast(), packings.len()) }
350}
351
352#[inline]
354const fn unwrap_mut<V: Underlier>(packings: &mut [PackedGf2<V>]) -> &mut [V] {
355 unsafe { slice::from_raw_parts_mut(packings.as_mut_ptr().cast(), packings.len()) }
357}
358
359impl<U: Underlier> Debug for PackedGf2<U> {
360 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
361 write!(f, "PackedGf2x{}(", U::BITS)?;
363 for (i, word) in self.0.words().iter().enumerate() {
364 if i > 0 {
365 write!(f, ", ")?;
366 }
367 let digits = Self::WORD_BITS / 4;
368 write!(f, "{:#0width$x}", word.to_u64(), width = 2 + digits)?;
369 }
370 write!(f, ")")
371 }
372}
373
374impl<U: Underlier> Distribution<PackedGf2<U>> for StandardUniform {
375 #[inline]
376 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> PackedGf2<U> {
377 PackedGf2(U::random(rng))
378 }
379}
380
381impl<U: Underlier> From<Gf2> for PackedGf2<U> {
382 #[inline]
383 fn from(value: Gf2) -> Self {
384 Self::broadcast(value)
385 }
386}
387
388impl<U: Underlier> Add for PackedGf2<U> {
389 type Output = Self;
390
391 #[inline]
392 #[allow(clippy::suspicious_arithmetic_impl)]
393 fn add(self, rhs: Self) -> Self {
394 Self(self.0.zip(rhs.0, |a, b| a ^ b))
396 }
397}
398
399impl<U: Underlier> Sub for PackedGf2<U> {
400 type Output = Self;
401
402 #[inline]
403 #[allow(clippy::suspicious_arithmetic_impl)]
404 fn sub(self, rhs: Self) -> Self {
405 self + rhs
407 }
408}
409
410impl<U: Underlier> Neg for PackedGf2<U> {
411 type Output = Self;
412
413 #[inline]
414 fn neg(self) -> Self {
415 self
417 }
418}
419
420impl<U: Underlier> Mul for PackedGf2<U> {
421 type Output = Self;
422
423 #[inline]
424 fn mul(self, rhs: Self) -> Self {
425 Self(self.0.zip(rhs.0, |a, b| a & b))
427 }
428}
429
430impl<U: Underlier> Add<Gf2> for PackedGf2<U> {
431 type Output = Self;
432
433 #[inline]
434 fn add(self, rhs: Gf2) -> Self {
435 self + Self::broadcast(rhs)
436 }
437}
438
439impl<U: Underlier> Sub<Gf2> for PackedGf2<U> {
440 type Output = Self;
441
442 #[inline]
443 fn sub(self, rhs: Gf2) -> Self {
444 self - Self::broadcast(rhs)
445 }
446}
447
448impl<U: Underlier> Mul<Gf2> for PackedGf2<U> {
449 type Output = Self;
450
451 #[inline]
452 fn mul(self, rhs: Gf2) -> Self {
453 self * Self::broadcast(rhs)
454 }
455}
456
457impl<U: Underlier> Div<Gf2> for PackedGf2<U> {
458 type Output = Self;
459
460 #[inline]
461 #[allow(clippy::suspicious_arithmetic_impl)]
462 fn div(self, rhs: Gf2) -> Self {
463 self * Self::broadcast(rhs.inverse())
465 }
466}
467
468impl<U: Underlier> DivAssign<Gf2> for PackedGf2<U> {
469 #[inline]
470 fn div_assign(&mut self, rhs: Gf2) {
471 *self = *self / rhs;
472 }
473}
474
475impl<U: Underlier, T: Into<Self>> AddAssign<T> for PackedGf2<U> {
476 #[inline]
477 fn add_assign(&mut self, rhs: T) {
478 *self = *self + rhs.into();
479 }
480}
481
482impl<U: Underlier, T: Into<Self>> SubAssign<T> for PackedGf2<U> {
483 #[inline]
484 fn sub_assign(&mut self, rhs: T) {
485 *self = *self - rhs.into();
486 }
487}
488
489impl<U: Underlier, T: Into<Self>> MulAssign<T> for PackedGf2<U> {
490 #[inline]
491 fn mul_assign(&mut self, rhs: T) {
492 *self = *self * rhs.into();
493 }
494}
495
496impl<U: Underlier> Sum for PackedGf2<U> {
497 #[inline]
498 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
499 iter.reduce(|x, y| x + y).unwrap_or(Self::ZERO)
500 }
501}
502
503impl<U: Underlier> Product for PackedGf2<U> {
504 #[inline]
505 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
506 iter.reduce(|x, y| x * y).unwrap_or(Self::ONE)
507 }
508}
509
510impl<U: Underlier> PrimeCharacteristicRing for PackedGf2<U> {
511 type PrimeSubfield = Gf2;
512
513 const ZERO: Self = Self(U::ZERO);
514 const ONE: Self = Self(U::ONES);
515 const TWO: Self = Self(U::ZERO);
517 const NEG_ONE: Self = Self(U::ONES);
519
520 #[inline]
521 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
522 Self::broadcast(f)
523 }
524
525 #[inline]
526 fn from_bool(b: bool) -> Self {
527 Self::broadcast(Gf2::from_bool(b))
528 }
529
530 characteristic_two_methods!();
531
532 #[inline]
533 fn square(&self) -> Self {
534 *self
536 }
537
538 #[inline]
539 fn andn(&self, y: &Self) -> Self {
540 Self(self.0.zip(y.0, |a, b| !a & b))
542 }
543
544 #[inline]
545 fn bool_check(&self) -> Self {
546 Self::ZERO
548 }
549}
550
551impl<U: Underlier> Algebra<Gf2> for PackedGf2<U> {}
552
553#[cfg(test)]
554mod tests {
555 use super::*;
556
557 macro_rules! packing_tests {
561 ($module:ident, $alias:ident, $underlier:ty, $word:ty, $words:literal, $width:literal) => {
562 mod $module {
563 use alloc::vec;
564 use alloc::vec::Vec;
565
566 use p3_field::PrimeCharacteristicRing;
567 use p3_field_testing::test_ring_with_eq_char2;
568 use proptest::prelude::*;
569
570 use crate::bitslice::underlier::Underlier;
571 use crate::{Gf2, $alias};
572
573 fn from_words(words: [$word; $words]) -> $alias {
575 $alias::new(<$underlier>::from_words_fn(|i| words[i]))
576 }
577
578 fn reference_add(a: $alias, b: $alias) -> $alias {
580 $alias::from_fn(|i| a.get(i) + b.get(i))
581 }
582
583 fn reference_mul(a: $alias, b: $alias) -> $alias {
585 $alias::from_fn(|i| a.get(i) * b.get(i))
586 }
587
588 fn reference_interleave(a: $alias, b: $alias, block: usize) -> ($alias, $alias) {
593 if block == $width {
594 return (a, b);
595 }
596 let mut first = vec![Gf2::ZERO; $width];
597 let mut second = vec![Gf2::ZERO; $width];
598
599 for s in 0..($width / block / 2) {
600 for t in 0..block {
601 first[(2 * s) * block + t] = a.get((2 * s) * block + t);
602 first[(2 * s + 1) * block + t] = b.get((2 * s) * block + t);
603 second[(2 * s) * block + t] = a.get((2 * s + 1) * block + t);
604 second[(2 * s + 1) * block + t] = b.get((2 * s + 1) * block + t);
605 }
606 }
607 (
608 $alias::from_fn(|i| first[i]),
609 $alias::from_fn(|i| second[i]),
610 )
611 }
612
613 fn specials() -> Vec<$alias> {
624 let mut lowest = $alias::ZERO;
625 lowest.set(0, Gf2::ONE);
626 let mut highest = $alias::ZERO;
627 highest.set($width - 1, Gf2::ONE);
628 vec![$alias::ZERO, $alias::ONE, lowest, highest]
629 }
630
631 #[test]
632 fn lane_i_is_bit_i() {
633 for lane in 0..$width {
635 let mut value = $alias::ZERO;
636 value.set(lane, Gf2::ONE);
637
638 let bits = <$word>::BITS as usize;
639 let mut expected = [0 as $word; $words];
640 expected[lane / bits] = 1 << (lane % bits);
641 assert_eq!(value.words(), expected, "lane {lane}");
642
643 for other in 0..$width {
645 let want = if other == lane { Gf2::ONE } else { Gf2::ZERO };
646 assert_eq!(value.get(other), want, "lane {lane}, read {other}");
647 }
648 }
649 }
650
651 #[test]
652 fn lane_i_is_bit_i_mod_8_of_byte_i_div_8() {
653 for lane in 0..$width {
658 let mut value = $alias::ZERO;
659 value.set(lane, Gf2::ONE);
660
661 let mut expected = [0u8; $width / 8];
662 expected[lane / 8] = 1 << (lane % 8);
663 assert_eq!($alias::as_bytes(&[value]), expected, "lane {lane}");
664 }
665 }
666
667 #[test]
668 fn constants_and_broadcast_agree_with_the_lane_view() {
669 assert_eq!($alias::ZERO.count_ones(), 0);
671 assert_eq!($alias::ONE.count_ones(), $width);
672 assert_eq!($alias::WIDTH, $width);
673
674 assert_eq!($alias::TWO, $alias::ZERO);
676 assert_eq!($alias::NEG_ONE, $alias::ONE);
677
678 assert_eq!($alias::broadcast(Gf2::ZERO), $alias::ZERO);
680 assert_eq!($alias::broadcast(Gf2::ONE), $alias::ONE);
681 assert_eq!($alias::from(Gf2::ONE), $alias::ONE);
682
683 let mut alternating = $alias::ZERO;
685 for lane in (0..$width).step_by(2) {
686 alternating.set(lane, Gf2::ONE);
687 }
688 let seen: Vec<Gf2> = alternating.lanes().collect();
689 assert_eq!(seen.len(), $width);
690 for (lane, value) in seen.into_iter().enumerate() {
691 let want = if lane % 2 == 0 { Gf2::ONE } else { Gf2::ZERO };
692 assert_eq!(value, want, "lane {lane}");
693 }
694 assert_eq!(alternating.count_ones() as usize, $width / 2);
695 }
696
697 #[test]
698 fn from_fn_inverts_get() {
699 for value in specials() {
701 assert_eq!($alias::from_fn(|i| value.get(i)), value);
702 }
703 }
704
705 #[test]
706 fn every_operation_matches_the_lane_view_at_the_corners() {
707 for a in specials() {
709 for b in specials() {
710 assert_eq!(a + b, reference_add(a, b));
711 assert_eq!(a - b, reference_add(a, b));
712 assert_eq!(a * b, reference_mul(a, b));
713 assert_eq!(a.xor(&b), reference_add(a, b));
714 assert_eq!(
715 a.andn(&b),
716 $alias::from_fn(|i| (Gf2::ONE - a.get(i)) * b.get(i))
717 );
718 }
719
720 assert_eq!(-a, a);
722 assert_eq!(a.double(), $alias::ZERO);
723 assert_eq!(a.square(), a);
724 assert_eq!(a.invert_or_zero(), a);
725 assert_eq!(a.bool_check(), $alias::ZERO);
726
727 assert_eq!(a.mul_2exp_u64(0), a);
729 assert_eq!(a.mul_2exp_u64(1), $alias::ZERO);
730 }
731 }
732
733 #[test]
734 fn scalar_operands_broadcast() {
735 for a in specials() {
737 for scalar in [Gf2::ZERO, Gf2::ONE] {
738 let broadcast = $alias::broadcast(scalar);
739 assert_eq!(a + scalar, a + broadcast);
740 assert_eq!(a - scalar, a - broadcast);
741 assert_eq!(a * scalar, a * broadcast);
742 }
743
744 assert_eq!(a / Gf2::ONE, a);
746 }
747 }
748
749 #[test]
750 fn interleave_matches_the_reference_at_every_block_size() {
751 let a = $alias::from_fn(|i| Gf2::from_bool(i % 2 == 0));
756 let b = $alias::from_fn(|i| Gf2::from_bool(i % 3 == 0));
757
758 let mut block = 1;
759 while block <= $width {
760 assert_eq!(
761 a.interleave(b, block),
762 reference_interleave(a, b, block),
763 "block length {block}"
764 );
765 block *= 2;
766 }
767
768 assert_eq!(a.interleave(b, $width), (a, b));
770 }
771
772 #[test]
773 #[should_panic = "lane index out of range"]
774 fn get_rejects_a_lane_beyond_the_width() {
775 let _lane = $alias::ZERO.get($width);
776 }
777
778 #[test]
779 #[should_panic = "lane index out of range"]
780 fn set_rejects_a_lane_beyond_the_width() {
781 let mut value = $alias::ZERO;
782 value.set($width, Gf2::ONE);
783 }
784
785 #[test]
786 #[should_panic = "block length must be a power of two dividing the width"]
787 fn interleave_rejects_a_block_wider_than_the_value() {
788 let _pair = $alias::ZERO.interleave($alias::ZERO, 2 * $width);
789 }
790
791 proptest! {
792 #![proptest_config(ProptestConfig::with_cases(64))]
793
794 #[test]
795 fn operations_match_the_lane_view(
796 left in prop::array::uniform::<_, $words>(any::<$word>()),
797 right in prop::array::uniform::<_, $words>(any::<$word>()),
798 ) {
799 let a = from_words(left);
800 let b = from_words(right);
801
802 prop_assert_eq!(a + b, reference_add(a, b));
804 prop_assert_eq!(a - b, reference_add(a, b));
805 prop_assert_eq!(a * b, reference_mul(a, b));
806 prop_assert_eq!(a.xor(&b), reference_add(a, b));
807
808 prop_assert_eq!(-a, a);
810 prop_assert_eq!(a.square(), a);
811 prop_assert_eq!(a.double(), $alias::ZERO);
812
813 prop_assert_eq!(a.words(), left);
815
816 let set = (0..$width).filter(|&i| a.get(i) == Gf2::ONE).count();
818 prop_assert_eq!(a.count_ones() as usize, set);
819 }
820
821 #[test]
822 fn interleave_matches_the_reference(
823 left in prop::array::uniform::<_, $words>(any::<$word>()),
824 right in prop::array::uniform::<_, $words>(any::<$word>()),
825 log_block in 0usize..=($width as usize).trailing_zeros() as usize,
826 ) {
827 let a = from_words(left);
828 let b = from_words(right);
829 let block = 1 << log_block;
830
831 prop_assert_eq!(
832 a.interleave(b, block),
833 reference_interleave(a, b, block)
834 );
835 }
836 }
837
838 test_ring_with_eq_char2!(
839 crate::$alias,
840 &[crate::$alias::ZERO],
841 &[crate::$alias::ONE]
842 );
843 }
844 };
845 }
846
847 packing_tests!(x8, PackedGf2x8, u8, u8, 1, 8);
848 packing_tests!(x16, PackedGf2x16, u16, u16, 1, 16);
849 packing_tests!(x32, PackedGf2x32, u32, u32, 1, 32);
850 packing_tests!(x64, PackedGf2x64, u64, u64, 1, 64);
851 packing_tests!(x128, PackedGf2x128, crate::M128, u64, 2, 128);
852 packing_tests!(x256, PackedGf2x256, crate::M256, u64, 4, 256);
853 packing_tests!(x512, PackedGf2x512, crate::M512, u64, 8, 512);
854
855 #[test]
856 fn interleave_follows_the_packed_field_convention() {
857 let a = PackedGf2x8::new(0xd3);
870 let b = PackedGf2x8::new(0x2c);
871
872 assert_eq!(pair(a.interleave(b, 1)), (0x59, 0x69));
877
878 assert_eq!(pair(a.interleave(b, 2)), (0x93, 0x3c));
883
884 assert_eq!(pair(a.interleave(b, 4)), (0xc3, 0x2d));
889
890 assert_eq!(pair(a.interleave(b, 8)), (0xd3, 0x2c));
892 }
893
894 fn pair(values: (PackedGf2x8, PackedGf2x8)) -> (u8, u8) {
896 (values.0.words()[0], values.1.words()[0])
897 }
898
899 #[test]
900 fn widths_are_the_advertised_sizes() {
901 assert_eq!(size_of::<PackedGf2x8>(), PackedGf2x8::WIDTH / 8);
907 assert_eq!(size_of::<PackedGf2x16>(), PackedGf2x16::WIDTH / 8);
908 assert_eq!(size_of::<PackedGf2x32>(), PackedGf2x32::WIDTH / 8);
909 assert_eq!(size_of::<PackedGf2x64>(), PackedGf2x64::WIDTH / 8);
910 assert_eq!(size_of::<PackedGf2x128>(), PackedGf2x128::WIDTH / 8);
911 assert_eq!(size_of::<PackedGf2x256>(), PackedGf2x256::WIDTH / 8);
912 assert_eq!(size_of::<PackedGf2x512>(), PackedGf2x512::WIDTH / 8);
913
914 assert_eq!(align_of::<PackedGf2x128>(), 16);
915 assert_eq!(align_of::<PackedGf2x256>(), 32);
916 assert_eq!(align_of::<PackedGf2x512>(), 64);
917 }
918
919 #[test]
920 fn a_wide_packing_is_a_run_of_narrow_ones() {
921 let mut wide = PackedGf2x512::ZERO;
927 for lane in [0usize, 65, 511] {
928 wide.set(lane, Gf2::ONE);
929 }
930
931 let narrow: &[PackedGf2x64] = wide.narrow();
932 assert_eq!(narrow.len(), 8);
933 assert_eq!(narrow[0].get(0), Gf2::ONE);
934 assert_eq!(narrow[1].get(1), Gf2::ONE);
935 assert_eq!(narrow[7].get(63), Gf2::ONE);
936 assert_eq!(
937 narrow.iter().map(PackedGf2x64::count_ones).sum::<u32>(),
938 wide.count_ones()
939 );
940
941 let bytes: &[PackedGf2x8] = wide.narrow();
943 assert_eq!(bytes.len(), 64);
944 assert_eq!(bytes[0].get(0), Gf2::ONE);
945 assert_eq!(bytes[8].get(1), Gf2::ONE);
946 assert_eq!(bytes[63].get(7), Gf2::ONE);
947
948 wide.narrow_mut::<u64>()[3].set(5, Gf2::ONE);
950 assert_eq!(wide.get(3 * 64 + 5), Gf2::ONE);
951 }
952
953 #[test]
954 fn a_run_of_wide_packings_is_a_run_of_narrow_ones() {
955 let wide: [PackedGf2x256; 2] = core::array::from_fn(|w| {
959 PackedGf2x256::from_fn(|i| Gf2::from_bool((w * 256 + i) % 5 == 0))
960 });
961
962 let narrow: &[PackedGf2x64] = PackedGf2x256::narrow_slice(&wide);
963 assert_eq!(narrow.len(), 8);
964 for lane in 0..512 {
965 let want = Gf2::from_bool(lane % 5 == 0);
966 assert_eq!(narrow[lane / 64].get(lane % 64), want, "lane {lane}");
967 }
968
969 let bytes = PackedGf2x256::as_bytes(&wide);
971 assert_eq!(bytes.len(), 64);
972 for lane in 0..512 {
973 let bit = (bytes[lane / 8] >> (lane % 8)) & 1;
974 assert_eq!(bit == 1, lane % 5 == 0, "lane {lane}");
975 }
976 }
977
978 #[test]
979 fn debug_prints_words_in_lane_order() {
980 let mut value = PackedGf2x128::ZERO;
985 value.set(0, Gf2::ONE);
986 value.set(64, Gf2::ONE);
987 assert_eq!(
988 alloc::format!("{value:?}"),
989 "PackedGf2x128(0x0000000000000001, 0x0000000000000001)"
990 );
991 }
992
993 #[test]
994 #[should_panic = "block out of range"]
995 fn block_mask_rejects_a_block_wider_than_half_a_word() {
996 let _mask = block_mask(64);
997 }
998}