binius_field/underlier/traits.rs
1// Copyright 2024-2025 Irreducible Inc.
2// Copyright 2026 The Binius Developers
3
4use std::{
5 fmt::Debug,
6 ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not},
7};
8
9use bytemuck::{NoUninit, TransparentWrapper, Zeroable};
10
11use super::U1;
12use crate::{Divisible, Random};
13
14/// A fixed-length vector of bits, whose length is a power of two.
15///
16/// This is the storage a binary field element lives in.
17/// An element is a bit pattern.
18/// This trait is the interface for holding that pattern and moving it around.
19///
20/// The same bits can be read two ways, and the interface serves both:
21///
22/// ```text
23/// BITS = 32
24///
25/// one field element [ -------------- x -------------- ]
26/// eight packed ones [ x7 x6 x5 x4 x3 x2 x1 x0 ]
27/// ```
28///
29/// Nothing here knows which reading is meant.
30/// The bitwise operators act on every bit at once, so they are correct under either.
31/// Addition in a binary field is exclusive or, which is why that operator is required.
32///
33/// # Why the length is a power of two
34///
35/// A value splits evenly in half, and each half splits again, down to single bits.
36/// The two shuffling operations below walk that ladder one rung at a time.
37/// A length like 24 bits would have no such ladder.
38///
39/// # Bit order
40///
41/// Bit 0 is the least significant.
42/// Every diagram here lists the low end first.
43/// That is the reverse of how a binary literal reads.
44pub trait Underlier:
45 Debug
46 + Default
47 + Eq
48 + Ord
49 + Copy
50 + Random
51 + NoUninit
52 + Zeroable
53 + Sized
54 + Send
55 + Sync
56 + 'static
57 + BitAnd<Self, Output = Self>
58 + BitAndAssign<Self>
59 + BitOr<Self, Output = Self>
60 + BitOrAssign<Self>
61 + BitXor<Self, Output = Self>
62 + BitXorAssign<Self>
63 + Not<Output = Self>
64 + Divisible<U1>
65{
66 /// Base-2 logarithm of the number of bits in a value.
67 const LOG_BITS: usize;
68
69 /// Number of bits in a value.
70 ///
71 /// This can be fewer than the bits of the type that stores it.
72 /// The one-, two-, and four-bit underliers each sit in a byte.
73 /// Their spare high bits carry nothing.
74 const BITS: usize = 1 << Self::LOG_BITS;
75
76 /// Every bit clear.
77 const ZERO: Self;
78
79 /// Bit 0 set, every other bit clear.
80 const ONE: Self;
81
82 /// Every bit set.
83 const ONES: Self;
84
85 /// Exchanges alternating blocks of two values.
86 ///
87 /// Cut both values into blocks of `2^log_block_len` bits, numbered from the low end.
88 /// The first result takes the even-numbered blocks of each value, one after the other.
89 /// The second result takes the odd-numbered ones the same way.
90 ///
91 /// ```text
92 /// BITS = 8, log_block_len = 1, so four blocks of two bits, low block first
93 ///
94 /// self [ a0 | a1 | a2 | a3 ]
95 /// other [ b0 | b1 | b2 | b3 ]
96 ///
97 /// first [ a0 | b0 | a2 | b2 ]
98 /// second [ a1 | b1 | a3 | b3 ]
99 /// ```
100 ///
101 /// This is one rung of the ladder that halves a value down to single bits.
102 /// Repeating it at every rung is what the transpose below does.
103 fn interleave(self, other: Self, log_block_len: usize) -> (Self, Self);
104
105 /// Separates two values into their even and odd blocks.
106 ///
107 /// Cut both values into blocks of `2^log_block_len` bits, numbered from the low end.
108 /// The first result collects every even-numbered block, this value's before the other's.
109 /// The second result collects every odd-numbered block the same way.
110 ///
111 /// ```text
112 /// BITS = 8, log_block_len = 0, so eight blocks of one bit, low bit first
113 ///
114 /// self [ a0 a1 a2 a3 a4 a5 a6 a7 ]
115 /// other [ b0 b1 b2 b3 b4 b5 b6 b7 ]
116 ///
117 /// first [ a0 a2 a4 a6 b0 b2 b4 b6 ]
118 /// second [ a1 a3 a5 a7 b1 b3 b5 b7 ]
119 /// ```
120 ///
121 /// Lay the two values out as the two rows of a matrix whose entries are blocks.
122 /// This reads that matrix out one column at a time, which is what makes it a transpose.
123 ///
124 /// # Panics
125 ///
126 /// Panics unless the block length is shorter than the whole value.
127 fn transpose(mut self, mut other: Self, log_block_len: usize) -> (Self, Self) {
128 assert!(log_block_len < Self::LOG_BITS);
129
130 // Start at the widest block and halve it each round, exchanging at every rung.
131 // After the last round every bit sits where its block index alone decides.
132 // That is what turns a sequence of exchanges into a transpose.
133 for log_block_len in (log_block_len..Self::LOG_BITS).rev() {
134 (self, other) = self.interleave(other, log_block_len);
135 }
136
137 (self, other)
138 }
139
140 /// Builds a value by filling it with narrower ones, low slot first.
141 ///
142 /// The two widths fix how many slots there are.
143 /// The closure is called exactly that many times.
144 #[inline]
145 fn from_fn<T>(f: impl FnMut(usize) -> T) -> Self
146 where
147 T: Underlier,
148 Self: Divisible<T>,
149 {
150 Self::from_iter((0..<Self as Divisible<T>>::N).map(f))
151 }
152}
153
154/// A type stored exactly as some underlier, and freely viewable as one.
155///
156/// A binary field element is a bit pattern with arithmetic attached.
157/// The bits sit in an underlier, and the element type wraps it to give those bits meaning.
158///
159/// Declaring that wrapper transparent means the two share an address, a size, and a bit pattern:
160///
161/// ```text
162/// element [ bits ] <- arithmetic attached
163/// underlier [ bits ] <- same address, same size, nothing to convert
164/// ```
165///
166/// So a value, a reference, or a whole slice can be viewed as the other side for free.
167/// Viewing a slice matters most, since it lets bulk code work on plain bits without copying.
168///
169/// The wrapping alone would be expressible with conversions in both directions.
170/// What those cannot give is the underlier's name.
171/// Generic code needs that name to state bounds against it.
172/// Carrying it as an associated type is what this trait adds.
173///
174/// # Safety
175///
176/// An implementor must have the same representation as the underlier it names.
177/// That is what makes casting a reference in either direction sound.
178pub unsafe trait UnderlierView:
179 TransparentWrapper<Self::Underlier> + Sized + Zeroable + Copy + Send + Sync + 'static
180{
181 /// The underlier holding this type's bits.
182 type Underlier: Underlier;
183
184 /// Views this value as its underlier.
185 #[inline]
186 fn to_underlier(self) -> Self::Underlier {
187 Self::peel(self)
188 }
189
190 /// Views a shared reference as one to its underlier.
191 #[inline]
192 fn to_underlier_ref(&self) -> &Self::Underlier {
193 Self::peel_ref(self)
194 }
195
196 /// Views a mutable reference as one to its underlier.
197 #[inline]
198 fn to_underlier_ref_mut(&mut self) -> &mut Self::Underlier {
199 Self::peel_mut(self)
200 }
201
202 /// Views a slice as a slice of underliers, without copying.
203 #[inline]
204 fn to_underliers_ref(val: &[Self]) -> &[Self::Underlier] {
205 Self::peel_slice(val)
206 }
207
208 /// Views a mutable slice as a mutable slice of underliers, without copying.
209 #[inline]
210 fn to_underliers_ref_mut(val: &mut [Self]) -> &mut [Self::Underlier] {
211 Self::peel_slice_mut(val)
212 }
213
214 /// Reads an underlier as this type.
215 #[inline]
216 fn from_underlier(val: Self::Underlier) -> Self {
217 Self::wrap(val)
218 }
219
220 /// Views a shared reference to an underlier as one to this type.
221 #[inline]
222 fn from_underlier_ref(val: &Self::Underlier) -> &Self {
223 Self::wrap_ref(val)
224 }
225
226 /// Views a mutable reference to an underlier as one to this type.
227 #[inline]
228 fn from_underlier_ref_mut(val: &mut Self::Underlier) -> &mut Self {
229 Self::wrap_mut(val)
230 }
231
232 /// Views a slice of underliers as a slice of this type, without copying.
233 #[inline]
234 fn from_underliers_ref(val: &[Self::Underlier]) -> &[Self] {
235 Self::wrap_slice(val)
236 }
237
238 /// Views a mutable slice of underliers as a mutable slice of this type, without copying.
239 #[inline]
240 fn from_underliers_ref_mut(val: &mut [Self::Underlier]) -> &mut [Self] {
241 Self::wrap_slice_mut(val)
242 }
243
244 /// Rewrites the bits through a function on the underlier, keeping this type on both ends.
245 #[inline]
246 fn mutate_underlier(self, f: impl FnOnce(Self::Underlier) -> Self::Underlier) -> Self {
247 Self::from_underlier(f(self.to_underlier()))
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use crate::underlier::{U2, U4};
255
256 #[test]
257 fn test_from_fn() {
258 assert_eq!(u32::from_fn(|_| U1::new(0)), 0);
259 assert_eq!(u32::from_fn(|i| U1::new((i % 2) as u8)), 0xaaaaaaaa);
260 assert_eq!(u32::from_fn(|_| U1::new(1)), u32::MAX);
261
262 assert_eq!(u32::from_fn(|_| U2::new(0)), 0);
263 assert_eq!(u32::from_fn(|_| U2::new(1)), 0x55555555);
264 assert_eq!(u32::from_fn(|_| U2::new(2)), 0xaaaaaaaa);
265 assert_eq!(u32::from_fn(|_| U2::new(3)), u32::MAX);
266 assert_eq!(u32::from_fn(|i| U2::new((i % 4) as u8)), 0xe4e4e4e4);
267
268 assert_eq!(u32::from_fn(|_| U4::new(0)), 0);
269 assert_eq!(u32::from_fn(|_| U4::new(1)), 0x11111111);
270 assert_eq!(u32::from_fn(|_| U4::new(8)), 0x88888888);
271 assert_eq!(u32::from_fn(|_| U4::new(31)), 0xffffffff);
272 assert_eq!(u32::from_fn(|i| U4::new(i as u8)), 0x76543210);
273
274 assert_eq!(u32::from_fn(|_| 0u8), 0);
275 assert_eq!(u32::from_fn(|_| 0xabu8), 0xabababab);
276 assert_eq!(u32::from_fn(|_| 255u8), 0xffffffff);
277 assert_eq!(u32::from_fn(|i| i as u8), 0x03020100);
278 }
279
280 /// Reads a value as its bits, low bit first, the way the diagrams above are drawn.
281 fn bits(value: u8) -> [u8; 8] {
282 std::array::from_fn(|i| (value >> i) & 1)
283 }
284
285 /// Packs bits given low bit first back into a value.
286 fn pack(bits: [u8; 8]) -> u8 {
287 bits.iter()
288 .enumerate()
289 .fold(0, |acc, (i, bit)| acc | (bit << i))
290 }
291
292 #[test]
293 fn interleave_exchanges_alternating_blocks() {
294 // Two values whose bits are all distinguishable by position.
295 let a = 0b1010_1010u8;
296 let b = 0b1100_1100u8;
297 let (av, bv) = (bits(a), bits(b));
298
299 // Blocks of one bit: the first result takes the even positions of each value in turn.
300 //
301 // first [ a0 b0 a2 b2 a4 b4 a6 b6 ]
302 // second [ a1 b1 a3 b3 a5 b5 a7 b7 ]
303 let (first, second) = a.interleave(b, 0);
304 assert_eq!(first, pack([av[0], bv[0], av[2], bv[2], av[4], bv[4], av[6], bv[6]]));
305 assert_eq!(second, pack([av[1], bv[1], av[3], bv[3], av[5], bv[5], av[7], bv[7]]));
306
307 // Blocks of two bits: the same pattern, one rung up the ladder.
308 //
309 // first [ a0 a1 | b0 b1 | a4 a5 | b4 b5 ]
310 let (first, second) = a.interleave(b, 1);
311 assert_eq!(first, pack([av[0], av[1], bv[0], bv[1], av[4], av[5], bv[4], bv[5]]));
312 assert_eq!(second, pack([av[2], av[3], bv[2], bv[3], av[6], av[7], bv[6], bv[7]]));
313 }
314
315 #[test]
316 fn transpose_separates_even_blocks_from_odd() {
317 let a = 0b1010_1010u8;
318 let b = 0b1100_1100u8;
319 let (av, bv) = (bits(a), bits(b));
320
321 // Single-bit blocks: every even bit lands in the first result, this value's before the
322 // other's, and every odd bit lands in the second.
323 //
324 // first [ a0 a2 a4 a6 b0 b2 b4 b6 ]
325 // second [ a1 a3 a5 a7 b1 b3 b5 b7 ]
326 let (first, second) = a.transpose(b, 0);
327 assert_eq!(first, pack([av[0], av[2], av[4], av[6], bv[0], bv[2], bv[4], bv[6]]));
328 assert_eq!(second, pack([av[1], av[3], av[5], av[7], bv[1], bv[3], bv[5], bv[7]]));
329 }
330
331 #[test]
332 fn transpose_at_the_widest_block_is_a_single_exchange() {
333 // One rung below the whole value leaves only one exchange to make, so the transpose and
334 // the interleave agree there.
335 let a = 0x3cu8;
336 let b = 0xa5u8;
337 assert_eq!(a.transpose(b, u8::LOG_BITS - 1), a.interleave(b, u8::LOG_BITS - 1));
338 }
339
340 #[test]
341 #[should_panic(expected = "log_block_len < Self::LOG_BITS")]
342 fn transpose_rejects_a_block_as_wide_as_the_value() {
343 // A block covering the whole value has no rung to stand on.
344 let _ = 0u8.transpose(0u8, u8::LOG_BITS);
345 }
346}