Skip to main content

p3_binary_field/bitslice/
packing.rs

1//! The bit-sliced `GF(2)` packings, eight through five hundred and twelve lanes wide.
2
3use 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
16/// Masks selecting the low `s` bits of every `2s`-bit block, indexed by `log2(s)`.
17///
18/// The interleave and the transpose swap a high block against the low one beside it:
19///
20/// ```text
21///     s = 1   ...0101 0101      every second bit
22///     s = 2   ...0011 0011      every second pair
23///     s = 4   ...00001111       every second nibble
24/// ```
25///
26/// Six entries suffice, since `s` never exceeds half a `u64`.
27const 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/// The mask selecting the low `block` bits of every `2 * block`-bit block.
37///
38/// The pattern repeats with period `2 * block`, and every word width is a multiple of it.
39/// So truncating the result to a narrower word keeps it intact.
40///
41/// # Panics
42/// Panics if `block` is not a power of two in `1 ..= 32`.
43#[inline]
44pub(super) const fn block_mask(block: usize) -> u64 {
45    // `trailing_zeros` is `log2` exactly because the caller passes a power of two.
46    assert!(block.is_power_of_two() && block <= 32, "block out of range");
47    BLOCK_MASKS[block.trailing_zeros() as usize]
48}
49
50/// Several independent elements of `GF(2)`, one per bit of a block of storage.
51///
52/// Lane `i` is bit `i mod B` of word `i / B`, counting from the least significant bit.
53/// Read as bytes, that puts lane `i` at bit `i mod 8` of byte `i / 8`.
54///
55/// This is the order a bit witness already has on the wire, so packing one moves no bits.
56///
57/// The arithmetic is what the field does to one bit, applied to the whole block at once:
58///
59/// ```text
60///     a + b   ->  a XOR b
61///     a * b   ->  a AND b
62///     a^2     ->  a
63/// ```
64#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
65#[repr(transparent)]
66#[must_use]
67pub struct PackedGf2<U: Underlier>(U);
68
69/// Eight elements of `GF(2)`, one per bit of a byte.
70pub type PackedGf2x8 = PackedGf2<u8>;
71
72/// Sixteen elements of `GF(2)`, one per bit of a 16-bit word.
73pub type PackedGf2x16 = PackedGf2<u16>;
74
75/// Thirty-two elements of `GF(2)`, one per bit of a 32-bit word.
76pub type PackedGf2x32 = PackedGf2<u32>;
77
78/// Sixty-four elements of `GF(2)`, one per bit of a 64-bit word.
79pub type PackedGf2x64 = PackedGf2<u64>;
80
81/// One hundred and twenty-eight elements of `GF(2)`, one per bit of a 128-bit block.
82pub type PackedGf2x128 = PackedGf2<M128>;
83
84/// Two hundred and fifty-six elements of `GF(2)`, one per bit of a 256-bit block.
85pub type PackedGf2x256 = PackedGf2<M256>;
86
87/// Five hundred and twelve elements of `GF(2)`, one per bit of a 512-bit block.
88pub type PackedGf2x512 = PackedGf2<M512>;
89
90impl<U: Underlier> PackedGf2<U> {
91    /// How many independent elements one value holds.
92    pub const WIDTH: usize = U::BITS;
93
94    /// Bits in one backing word.
95    pub(super) const WORD_BITS: usize = <U::Word as Word>::BITS;
96
97    /// The packing over a block of storage, read lane for bit.
98    #[inline]
99    pub const fn new(bits: U) -> Self {
100        Self(bits)
101    }
102
103    /// The block of storage behind the packing.
104    #[inline]
105    pub const fn to_bits(self) -> U {
106        self.0
107    }
108
109    /// The backing words, lowest lanes first.
110    #[inline]
111    #[must_use]
112    pub fn words(&self) -> &[U::Word] {
113        self.0.words()
114    }
115
116    /// The backing words, lowest lanes first.
117    ///
118    /// Every bit pattern is a valid packing, so writing here cannot break an invariant.
119    #[inline]
120    pub fn words_mut(&mut self) -> &mut [U::Word] {
121        self.0.words_mut()
122    }
123
124    /// The element in the given lane.
125    ///
126    /// # Panics
127    /// Panics if the lane index is not below the width.
128    #[inline]
129    pub fn get(&self, lane: usize) -> Gf2 {
130        assert!(lane < Self::WIDTH, "lane index out of range");
131
132        // Bring the lane's bit down to the bottom of its word and keep only it.
133        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    /// Overwrite the element in the given lane.
139    ///
140    /// # Panics
141    /// Panics if the lane index is not below the width.
142    #[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        // Clear the lane, then drop the new bit in.
149        // Branch-free, so the stored bit never steers control flow.
150        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    /// Build a value lane by lane.
157    #[inline]
158    pub fn from_fn(mut f: impl FnMut(usize) -> Gf2) -> Self {
159        let mut out = Self(U::ZERO);
160
161        // One shift-and-insert per lane, lowest lane first.
162        for lane in 0..Self::WIDTH {
163            out.set(lane, f(lane));
164        }
165        out
166    }
167
168    /// The same element in every lane.
169    #[inline]
170    pub fn broadcast(value: Gf2) -> Self {
171        // Negating the bit spreads it over the whole word: one becomes all ones, zero none.
172        // Branch-free, so a witness bit never steers control flow.
173        let word = U::Word::from_u64(0u64.wrapping_sub(u64::from(value.to_bit())));
174        Self(U::from_words_fn(|_| word))
175    }
176
177    /// The lanes, lowest first.
178    #[inline]
179    pub fn lanes(self) -> impl Iterator<Item = Gf2> {
180        (0..Self::WIDTH).map(move |lane| self.get(lane))
181    }
182
183    /// How many lanes hold one.
184    #[inline]
185    #[must_use]
186    pub fn count_ones(&self) -> u32 {
187        // Popcount is per word, so the totals add.
188        self.0.words().iter().map(|word| word.count_ones()).sum()
189    }
190
191    /// The lane-wise inverse, with zero sent to zero.
192    ///
193    /// One is the only invertible element of `GF(2)` and it is its own inverse.
194    ///
195    /// Extending the map by `0^-1 = 0` therefore makes it the identity on every lane.
196    #[inline]
197    pub const fn invert_or_zero(self) -> Self {
198        self
199    }
200
201    /// This value read as a run of narrower packings, lowest lanes first.
202    ///
203    /// A wide block is narrow ones side by side: lane `i` is lane `i mod w` of part `i / w`.
204    #[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    /// This value read as a run of narrower packings, lowest lanes first.
213    #[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    /// A run of these packings read as a run of narrower ones, lowest lanes first.
222    ///
223    /// The same bytes carry the same lanes in order, so a narrower read copies nothing.
224    #[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    /// A run of these packings read as a run of narrower ones, lowest lanes first.
233    #[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    /// A run of these packings read as bytes, lowest lanes first.
242    ///
243    /// Byte `k` holds lanes `8k .. 8k + 8`, lowest lane at the lowest bit.
244    #[inline]
245    #[must_use]
246    pub const fn as_bytes(slice: &[Self]) -> &[u8] {
247        // A packing is exactly its words, so the byte count is the lane count over eight.
248        //
249        // A lane is a bit of a word value, and a byte index is a memory offset.
250        // The two orders agree only on a little-endian target.
251        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        // SAFETY: the assertion pins the run at exactly `len` initialised bytes.
261        // The byte slice borrows the same region for the same lifetime.
262        unsafe { slice::from_raw_parts(slice.as_ptr().cast::<u8>(), len) }
263    }
264
265    /// Cut both operands into chunks of `block_len` lanes and interleave the chunks.
266    ///
267    /// Cut the stack of the two into two-by-two matrices of chunks, and transpose each:
268    ///
269    /// ```text
270    ///     A = [x0, y0, x1, y1]
271    ///     B = [x2, y2, x3, y3]
272    ///
273    ///     block_len = 1  ->  ([x0, x2, x1, x3], [y0, y2, y1, y3])
274    /// ```
275    ///
276    /// A chunk as wide as the value leaves both operands untouched.
277    ///
278    /// This is the convention the packed-field trait fixes.
279    /// So a caller can swap a bit-sliced packing for any other and see one permutation.
280    ///
281    /// # Panics
282    /// Panics unless the chunk length is a power of two no larger than the width.
283    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        // One chunk each means there is nothing to pair up.
290        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    /// One output value of the interleave: the first when `take` is zero, the second when one.
300    ///
301    /// Output chunk `c` comes from chunk `2 * (c / 2) + take`.
302    /// Of the left operand when `c` is even, and of the right operand when it is odd.
303    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            // The first output lane this word carries.
309            let first = w * bits;
310
311            if block_len >= bits {
312                // A chunk spans whole words, so the word is copied, not rearranged.
313                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                // Chunks sit inside a word.
319                // This word takes every other chunk of each operand, starting at `take`.
320                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/// A run of blocks read as a run of packings over them.
330///
331/// A packing is a transparent wrapper, so the two have one layout and nothing moves.
332#[inline]
333const fn wrap<V: Underlier>(parts: &[V]) -> &[PackedGf2<V>] {
334    // SAFETY: `repr(transparent)` gives one size and alignment, and every block is valid.
335    unsafe { slice::from_raw_parts(parts.as_ptr().cast(), parts.len()) }
336}
337
338/// A run of blocks read as a run of packings over them.
339#[inline]
340const fn wrap_mut<V: Underlier>(parts: &mut [V]) -> &mut [PackedGf2<V>] {
341    // SAFETY: as above, and the exclusive borrow is not duplicated.
342    unsafe { slice::from_raw_parts_mut(parts.as_mut_ptr().cast(), parts.len()) }
343}
344
345/// A run of packings read as a run of the blocks behind them.
346#[inline]
347const fn unwrap<V: Underlier>(packings: &[PackedGf2<V>]) -> &[V] {
348    // SAFETY: the inverse of the transparent wrapping above.
349    unsafe { slice::from_raw_parts(packings.as_ptr().cast(), packings.len()) }
350}
351
352/// A run of packings read as a run of the blocks behind them.
353#[inline]
354const fn unwrap_mut<V: Underlier>(packings: &mut [PackedGf2<V>]) -> &mut [V] {
355    // SAFETY: the inverse of the transparent wrapping above.
356    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        // Words print in lane order, so the leftmost word holds the lowest lanes.
362        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        // Addition in `GF(2)` is exclusive or, lane by lane.
395        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        // Subtraction coincides with addition in characteristic 2.
406        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        // `-x = x` in characteristic 2.
416        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        // Multiplication in `GF(2)` is conjunction, lane by lane.
426        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        // One is the only invertible element, and inverting zero panics.
464        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    // The characteristic is 2, so `TWO = ONE + ONE = ZERO`.
516    const TWO: Self = Self(U::ZERO);
517    // The characteristic is 2, so `NEG_ONE = ONE`.
518    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        // `0^2 = 0` and `1^2 = 1`, so squaring fixes every lane.
535        *self
536    }
537
538    #[inline]
539    fn andn(&self, y: &Self) -> Self {
540        // `(1 - x) * y` is `NOT x AND y`, one instruction per word.
541        Self(self.0.zip(y.0, |a, b| !a & b))
542    }
543
544    #[inline]
545    fn bool_check(&self) -> Self {
546        // Every element of `GF(2)` is a bit, so the booleanity residue is always zero.
547        Self::ZERO
548    }
549}
550
551impl<U: Underlier> Algebra<Gf2> for PackedGf2<U> {}
552
553#[cfg(test)]
554mod tests {
555    use super::*;
556
557    /// One test module per packing width.
558    ///
559    /// Each scenario is stated once, against a reference built from the lane accessors.
560    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                /// A packing over the given backing words, lowest lanes first.
574                fn from_words(words: [$word; $words]) -> $alias {
575                    $alias::new(<$underlier>::from_words_fn(|i| words[i]))
576                }
577
578                /// The lane-wise sum, built one lane at a time.
579                fn reference_add(a: $alias, b: $alias) -> $alias {
580                    $alias::from_fn(|i| a.get(i) + b.get(i))
581                }
582
583                /// The lane-wise product, built one lane at a time.
584                fn reference_mul(a: $alias, b: $alias) -> $alias {
585                    $alias::from_fn(|i| a.get(i) * b.get(i))
586                }
587
588                /// The interleave written straight from its definition.
589                ///
590                /// Chunk `2s` of each operand pairs with chunk `2s + 1`, transposed.
591                /// The first output takes both chunks `2s`, the second both `2s + 1`.
592                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                /// The bit patterns a random search is unlikely to reach.
614                ///
615                /// Each one drives the lane map to an extreme:
616                ///
617                /// ```text
618                ///     all zeros     no lane set
619                ///     all ones      every lane set
620                ///     lane 0        the lowest bit of the first word
621                ///     lane W-1      the highest bit of the last word
622                /// ```
623                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                    // Setting one lane lights exactly one bit, at `i mod B` of word `i / B`.
634                    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                        // And reading it back names the same lane, with the rest still zero.
644                        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                    // The byte view: eight lanes to the byte, lowest lane at the lowest bit.
654                    //
655                    //     | lane:  0 1 2 3 4 5 6 7 | 8 9 ...
656                    //     | byte:  <--- byte 0 ---> <- byte 1 ...
657                    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                    // Zero is every lane clear, one is every lane set.
670                    assert_eq!($alias::ZERO.count_ones(), 0);
671                    assert_eq!($alias::ONE.count_ones(), $width);
672                    assert_eq!($alias::WIDTH, $width);
673
674                    // Characteristic 2: `1 + 1 = 0` and `-1 = 1`.
675                    assert_eq!($alias::TWO, $alias::ZERO);
676                    assert_eq!($alias::NEG_ONE, $alias::ONE);
677
678                    // Broadcasting a scalar puts it in every lane.
679                    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                    // The lane iterator walks lanes in order, lowest first.
684                    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                    // Rebuilding from its own lanes returns the same value.
700                    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                    // The kernels are lane-local, so the corners match lane by lane.
708                    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                        // Negation, doubling and squaring are settled by the characteristic.
721                        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                        // Multiplying by a power of two collapses everything above the zeroth.
728                        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                    // Mixing a scalar in must act as if it had been broadcast first.
736                    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                        // Dividing by one is the identity; one is the only invertible scalar.
745                        assert_eq!(a / Gf2::ONE, a);
746                    }
747                }
748
749                #[test]
750                fn interleave_matches_the_reference_at_every_block_size() {
751                    // Distinguishable lanes, so a misplaced one shows:
752                    //
753                    //     | a: lane i set iff i is even
754                    //     | b: lane i set iff i is a multiple of three
755                    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                    // A block as wide as the value leaves both operands where they were.
769                    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                        // Every binary operation is the scalar one applied lane by lane.
803                        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                        // And every unary one is fixed by the characteristic.
809                        prop_assert_eq!(-a, a);
810                        prop_assert_eq!(a.square(), a);
811                        prop_assert_eq!(a.double(), $alias::ZERO);
812
813                        // Words in, words out.
814                        prop_assert_eq!(a.words(), left);
815
816                        // Popcount over the lanes is popcount over the words.
817                        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        // Invariant: the permutation the packed-field trait fixes for every packing.
858        //
859        // Output chunk `c` is chunk `2 * (c / 2)` of the left operand when `c` is even.
860        // Of the right operand when `c` is odd.
861        //
862        // The second output takes the odd source chunks the same way.
863        //
864        // The expectations below are worked out by hand from those sentences.
865        // So they share no model with the implementation.
866        //
867        //     | a = 0xd3 = lanes 0 1 . . 4 . 6 7
868        //     | b = 0x2c = lanes . . 2 3 . 5 . .
869        let a = PackedGf2x8::new(0xd3);
870        let b = PackedGf2x8::new(0x2c);
871
872        // Chunks of one lane:
873        //
874        //     | first  = a0 b0 a2 b2 a4 b4 a6 b6 = 1 0 0 1 1 0 1 0 = 0x59
875        //     | second = a1 b1 a3 b3 a5 b5 a7 b7 = 1 0 0 1 0 1 1 0 = 0x69
876        assert_eq!(pair(a.interleave(b, 1)), (0x59, 0x69));
877
878        // Chunks of two lanes, so whole pairs move:
879        //
880        //     | first  = a[0..2] b[0..2] a[4..6] b[4..6] = 11 00 10 01 = 0x93
881        //     | second = a[2..4] b[2..4] a[6..8] b[6..8] = 00 11 11 00 = 0x3c
882        assert_eq!(pair(a.interleave(b, 2)), (0x93, 0x3c));
883
884        // Chunks of four lanes: one half of each operand into each output.
885        //
886        //     | first  = a[0..4] b[0..4] = 1100 0011 = 0xc3
887        //     | second = a[4..8] b[4..8] = 1011 0100 = 0x2d
888        assert_eq!(pair(a.interleave(b, 4)), (0xc3, 0x2d));
889
890        // One chunk each leaves both operands alone.
891        assert_eq!(pair(a.interleave(b, 8)), (0xd3, 0x2c));
892    }
893
894    /// The backing bytes of an interleaved pair, for comparison against literals.
895    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        // One bit per lane and nothing else, aligned to the register that holds it:
902        //
903        //     | width   bytes   alignment
904        //     |     8       1        1
905        //     |   512      64       64
906        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        // Invariant: narrowing moves no bits, so a 512-lane value splits as
922        //
923        //     | lane 0    -> 64-lane packing 0, lane 0
924        //     | lane 65   -> 64-lane packing 1, lane 1
925        //     | lane 511  -> 64-lane packing 7, lane 63
926        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        // Every intermediate width sees the same lanes in the same order.
942        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        // Writing through a narrow view writes the wide value.
949        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        // The same relation across a whole buffer, which is what makes a re-read free.
956        //
957        //     | 512 lanes  ->  8 packings of 64 lanes  ->  64 packings of 8 lanes
958        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        // And the byte view of the same buffer agrees with the lane order.
970        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        // The lowest lanes print first, so the text reads in lane order:
981        //
982        //     | lane 0 set   -> the first word ends in one
983        //     | lane 64 set  -> the second word ends in one
984        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}