binius_field/
packed_extension.rs

1// Copyright 2023-2025 Irreducible Inc.
2
3use crate::{
4	ExtensionField, Field, PackedField,
5	as_packed_field::PackScalar,
6	underlier::{Divisible, WithUnderlier},
7};
8
9/// This is a marker trait showing that [`PackedField`] can be safely cast to a slice of scalars.
10///
11/// Not all packed fields can index individual scalar elements. Notably, packed fields of
12/// $\mathbb{F}_2$ elements can pack multiple scalars into a single byte.
13///
14///
15/// # Safety
16///
17/// In order for the above relation to be guaranteed, the memory representation of a slice of
18/// `PackedExtensionIndexable` elements must be the same as a slice of the underlying scalar
19/// elements, differing only in the slice lengths.
20unsafe trait PackedFieldIndexable: PackedField {}
21
22unsafe impl<S, P> PackedFieldIndexable for P
23where
24	S: Field,
25	P: PackedDivisible<S, Scalar = S>,
26{
27}
28
29/// Check if `P` implements `PackedFieldIndexable`.
30/// This functions gets optimized out by the compiler so if it is used in a generic context
31/// as an `if` condition, the non-meaningful branch will be optimized out.
32#[inline(always)]
33#[allow(clippy::redundant_clone)]
34pub fn is_packed_field_indexable<P: PackedField>() -> bool {
35	// Use a hack that array of copyable elements won't call clone when the array is cloned.
36
37	struct X<T> {
38		cloned: bool,
39		_pd: std::marker::PhantomData<T>,
40	}
41
42	impl<T> Clone for X<T> {
43		fn clone(&self) -> Self {
44			Self {
45				cloned: true,
46				_pd: std::marker::PhantomData,
47			}
48		}
49	}
50
51	impl<T: PackedFieldIndexable> Copy for X<T> {}
52
53	let arr = [X::<P> {
54		cloned: false,
55		_pd: std::marker::PhantomData,
56	}];
57	let cloned = arr.clone();
58
59	!cloned[0].cloned
60}
61
62/// Trait represents a relationship between a packed struct of field elements and a packed struct
63/// of elements from an extension field.
64///
65/// This trait guarantees that one packed type has the same
66/// memory representation as the other, differing only in the scalar type and preserving the order
67/// of smaller elements.
68///
69/// This trait relation guarantees that the following iterators yield the same sequence of scalar
70/// elements:
71///
72/// ```
73/// use binius_field::{ExtensionField, PackedExtension, PackedField, Field};
74///
75/// fn ext_then_bases<'a, F, PE>(packed: &'a PE) -> impl Iterator<Item=F> + 'a
76///     where
77///         PE: PackedField<Scalar: ExtensionField<F>>,
78///         F: Field,
79/// {
80///     packed.iter().flat_map(|ext| ext.into_iter_bases())
81/// }
82///
83/// fn cast_then_iter<'a, F, PE>(packed: &'a PE) -> impl Iterator<Item=F> + 'a
84///     where
85///         PE: PackedExtension<F>,
86///         F: Field,
87/// {
88///     PE::cast_base_ref(packed).into_iter()
89/// }
90/// ```
91///
92/// # Safety
93///
94/// In order for the above relation to be guaranteed, the memory representation of
95/// `PackedExtensionField` element must be the same as a slice of the underlying `PackedField`
96/// element.
97pub trait PackedExtension<FS: Field>: PackedField<Scalar: ExtensionField<FS>> {
98	type PackedSubfield: PackedField<Scalar = FS>;
99
100	fn cast_bases(packed: &[Self]) -> &[Self::PackedSubfield];
101	fn cast_bases_mut(packed: &mut [Self]) -> &mut [Self::PackedSubfield];
102
103	fn cast_exts(packed: &[Self::PackedSubfield]) -> &[Self];
104	fn cast_exts_mut(packed: &mut [Self::PackedSubfield]) -> &mut [Self];
105
106	fn cast_base(self) -> Self::PackedSubfield;
107	fn cast_base_ref(&self) -> &Self::PackedSubfield;
108	fn cast_base_mut(&mut self) -> &mut Self::PackedSubfield;
109
110	fn cast_ext(base: Self::PackedSubfield) -> Self;
111	fn cast_ext_ref(base: &Self::PackedSubfield) -> &Self;
112	fn cast_ext_mut(base: &mut Self::PackedSubfield) -> &mut Self;
113
114	#[inline(always)]
115	fn cast_base_arr<const N: usize>(packed: [Self; N]) -> [Self::PackedSubfield; N] {
116		packed.map(Self::cast_base)
117	}
118
119	#[inline(always)]
120	fn cast_base_arr_ref<const N: usize>(packed: &[Self; N]) -> &[Self::PackedSubfield; N] {
121		Self::cast_bases(packed)
122			.try_into()
123			.expect("array has size N")
124	}
125
126	#[inline(always)]
127	fn cast_base_arr_mut<const N: usize>(packed: &mut [Self; N]) -> &mut [Self::PackedSubfield; N] {
128		Self::cast_bases_mut(packed)
129			.try_into()
130			.expect("array has size N")
131	}
132
133	#[inline(always)]
134	fn cast_ext_arr<const N: usize>(packed: [Self::PackedSubfield; N]) -> [Self; N] {
135		packed.map(Self::cast_ext)
136	}
137
138	#[inline(always)]
139	fn cast_ext_arr_ref<const N: usize>(packed: &[Self::PackedSubfield; N]) -> &[Self; N] {
140		Self::cast_exts(packed)
141			.try_into()
142			.expect("array has size N")
143	}
144
145	#[inline(always)]
146	fn cast_ext_arr_mut<const N: usize>(packed: &mut [Self::PackedSubfield; N]) -> &mut [Self; N] {
147		Self::cast_exts_mut(packed)
148			.try_into()
149			.expect("array has size N")
150	}
151}
152
153impl<PT, FS> PackedExtension<FS> for PT
154where
155	FS: Field,
156	PT: PackedField<Scalar: ExtensionField<FS>> + WithUnderlier<Underlier: PackScalar<FS>>,
157{
158	type PackedSubfield = <PT::Underlier as PackScalar<FS>>::Packed;
159
160	fn cast_bases(packed: &[Self]) -> &[Self::PackedSubfield] {
161		Self::PackedSubfield::from_underliers_ref(Self::to_underliers_ref(packed))
162	}
163
164	fn cast_bases_mut(packed: &mut [Self]) -> &mut [Self::PackedSubfield] {
165		Self::PackedSubfield::from_underliers_ref_mut(Self::to_underliers_ref_mut(packed))
166	}
167
168	fn cast_exts(base: &[Self::PackedSubfield]) -> &[Self] {
169		Self::from_underliers_ref(Self::PackedSubfield::to_underliers_ref(base))
170	}
171
172	fn cast_exts_mut(base: &mut [Self::PackedSubfield]) -> &mut [Self] {
173		Self::from_underliers_ref_mut(Self::PackedSubfield::to_underliers_ref_mut(base))
174	}
175
176	fn cast_base(self) -> Self::PackedSubfield {
177		Self::PackedSubfield::from_underlier(self.to_underlier())
178	}
179
180	fn cast_base_ref(&self) -> &Self::PackedSubfield {
181		Self::PackedSubfield::from_underlier_ref(self.to_underlier_ref())
182	}
183
184	fn cast_base_mut(&mut self) -> &mut Self::PackedSubfield {
185		Self::PackedSubfield::from_underlier_ref_mut(self.to_underlier_ref_mut())
186	}
187
188	fn cast_ext(base: Self::PackedSubfield) -> Self {
189		Self::from_underlier(base.to_underlier())
190	}
191
192	fn cast_ext_ref(base: &Self::PackedSubfield) -> &Self {
193		Self::from_underlier_ref(base.to_underlier_ref())
194	}
195
196	fn cast_ext_mut(base: &mut Self::PackedSubfield) -> &mut Self {
197		Self::from_underlier_ref_mut(base.to_underlier_ref_mut())
198	}
199}
200
201/// Convenient type alias that returns the packed field type for the scalar field `F` and packed
202/// extension `P`.
203pub type PackedSubfield<P, F> = <P as PackedExtension<F>>::PackedSubfield;
204
205/// Recast a packed field from one subfield of a packed extension to another.
206pub fn recast_packed<P, FSub1, FSub2>(elem: PackedSubfield<P, FSub1>) -> PackedSubfield<P, FSub2>
207where
208	P: PackedField + PackedExtension<FSub1> + PackedExtension<FSub2>,
209	P::Scalar: ExtensionField<FSub1> + ExtensionField<FSub2>,
210	FSub1: Field,
211	FSub2: Field,
212{
213	<P as PackedExtension<FSub2>>::cast_base(<P as PackedExtension<FSub1>>::cast_ext(elem))
214}
215
216/// Recast a slice of packed field elements from one subfield of a packed extension to another.
217pub fn recast_packed_slice<P, FSub1, FSub2>(
218	elems: &[PackedSubfield<P, FSub1>],
219) -> &[PackedSubfield<P, FSub2>]
220where
221	P: PackedField + PackedExtension<FSub1> + PackedExtension<FSub2>,
222	P::Scalar: ExtensionField<FSub1> + ExtensionField<FSub2>,
223	FSub1: Field,
224	FSub2: Field,
225{
226	<P as PackedExtension<FSub2>>::cast_bases(<P as PackedExtension<FSub1>>::cast_exts(elems))
227}
228
229/// Recast a mutable slice of packed field elements from one subfield of a packed extension to
230/// another.
231pub fn recast_packed_mut<P, FSub1, FSub2>(
232	elems: &mut [PackedSubfield<P, FSub1>],
233) -> &mut [PackedSubfield<P, FSub2>]
234where
235	P: PackedField + PackedExtension<FSub1> + PackedExtension<FSub2>,
236	P::Scalar: ExtensionField<FSub1> + ExtensionField<FSub2>,
237	FSub1: Field,
238	FSub2: Field,
239{
240	<P as PackedExtension<FSub2>>::cast_bases_mut(<P as PackedExtension<FSub1>>::cast_exts_mut(
241		elems,
242	))
243}
244
245/// This trait is a shorthand for the case `PackedExtension<P::Scalar, PackedSubfield = P>` which is
246/// a quite common case in our codebase.
247pub trait RepackedExtension<P: PackedField>:
248	PackedField<Scalar: ExtensionField<P::Scalar>> + PackedExtension<P::Scalar, PackedSubfield = P>
249{
250}
251
252impl<PT1, PT2> RepackedExtension<PT1> for PT2
253where
254	PT1: PackedField,
255	PT2: PackedExtension<PT1::Scalar, PackedSubfield = PT1, Scalar: ExtensionField<PT1::Scalar>>,
256{
257}
258
259/// A marker trait that represents a relationship between a packed struct of field elements and a
260/// smaller packed struct the same field elements.
261///
262/// This trait can be used to safely cast memory slices from larger packed fields to smaller ones.
263///
264/// # Safety
265///
266/// In order for the above relation to be guaranteed, the memory representation of a slice of
267/// `PackedDivisible` elements must be the same as a slice of the underlying `PackedField`
268/// elements, differing only in the slice lengths.
269unsafe trait PackedDivisible<P>: PackedField
270where
271	P: PackedField<Scalar = Self::Scalar>,
272{
273}
274
275unsafe impl<PT1, PT2> PackedDivisible<PT2> for PT1
276where
277	PT2: PackedField + WithUnderlier,
278	PT1: PackedField<Scalar = PT2::Scalar> + WithUnderlier<Underlier: Divisible<PT2::Underlier>>,
279{
280}