binius_field/underlier/maskable.rs
1// Copyright 2026 The Binius Developers
2
3use bytemuck::Zeroable;
4
5use super::divisible::Divisible;
6
7/// Branchless conditional lane selection for fields and packed fields.
8///
9/// `Maskable<T>` keeps a chosen subset of a value's `T`-lanes and zeroes the rest, against a
10/// precomputed [`Self::Mask`]. It is the high-level, underlier-free primitive behind the branchless
11/// masked accumulation in the shift protocol: a mask is built once with [`make_mask`] and reused
12/// across many [`select`] calls, so the per-call cost is a single bitwise AND on a binary field.
13///
14/// It is a parent trait of [`Field`](crate::Field) (`Maskable<Self>`, a single lane) and
15/// [`PackedField`](crate::PackedField) (`Maskable<Self::Scalar>`, one entry per packed lane),
16/// mirroring how [`Divisible`] is a parent trait of both.
17///
18/// [`make_mask`]: Maskable::make_mask
19/// [`select`]: Maskable::select
20pub trait Maskable<T>: Divisible<T> + Copy + Zeroable {
21 /// A precomputed mask selecting which `T`-lanes [`select`](Maskable::select) keeps.
22 ///
23 /// `Send + Sync` so a precomputed mask table can be shared across threads (e.g. a parallel
24 /// fold over a precomputed `Vec<Self::Mask>`).
25 type Mask: Send + Sync;
26
27 /// Builds a mask from per-lane boolean selectors, in LSB-to-MSB lane order (the same ordering
28 /// as [`Divisible`]). Consumes at most [`Divisible::N`] selectors; any lane past the end of the
29 /// iterator, or whose selector is `false`, is not selected.
30 fn make_mask(selectors: impl Iterator<Item = bool>) -> Self::Mask;
31
32 /// Returns a value keeping the lanes selected when `mask` was built and zeroing the rest.
33 ///
34 /// For a `mask` built by `Self::make_mask(selectors)`, this equals
35 ///
36 /// ```text
37 /// Self::from_iter(zip(self.ref_iter(), selectors)
38 /// .map(|(val, selected)| if selected { val } else { <zero> }))
39 /// ```
40 ///
41 /// but branchless: on a binary field each selected lane of the mask is all-ones and each
42 /// unselected lane all-zeros, so this is a single bitwise AND.
43 fn select(&self, mask: &Self::Mask) -> Self;
44}