Skip to main content

p3_field/extension/
packed_binomial_extension.rs

1use alloc::vec::Vec;
2use core::array;
3use core::iter::{Product, Sum};
4use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5
6use p3_util::{flatten_to_base, reconstitute_from_base};
7use rand::distr::{Distribution, StandardUniform};
8
9use super::{BinomialExtensionField, PackedExtField, binomial_mul, vector_add, vector_sub};
10use crate::extension::{Binomial, BinomiallyExtendable, binomial_square};
11use crate::{
12    Algebra, BasedVectorSpace, Dup, Field, PackedField, PackedFieldExtension, PackedValue, Powers,
13    PrimeCharacteristicRing, field_to_array,
14};
15
16/// Packed binomial extension field `F[X] / (X^D - W)`, one element per SIMD lane.
17///
18/// Type alias for the unified [`PackedExtField`] with `Shape = Binomial<F>`.
19pub type PackedBinomialExtensionField<F, PF, const D: usize> =
20    PackedExtField<F, PF, D, Binomial<F>>;
21
22impl<F: Field, PF: PackedField<Scalar = F>, const D: usize> Default
23    for PackedBinomialExtensionField<F, PF, D>
24{
25    #[inline]
26    fn default() -> Self {
27        Self::new(array::from_fn(|_| PF::ZERO))
28    }
29}
30
31impl<F: Field, PF: PackedField<Scalar = F>, const D: usize> From<BinomialExtensionField<F, D>>
32    for PackedBinomialExtensionField<F, PF, D>
33{
34    #[inline]
35    fn from(x: BinomialExtensionField<F, D>) -> Self {
36        Self::new(x.value.map(Into::into))
37    }
38}
39
40impl<F: Field, PF: PackedField<Scalar = F>, const D: usize> From<PF>
41    for PackedBinomialExtensionField<F, PF, D>
42{
43    #[inline]
44    fn from(x: PF) -> Self {
45        Self::new(field_to_array(x))
46    }
47}
48
49impl<F: Field, PF: PackedField<Scalar = F>, const D: usize>
50    Distribution<PackedBinomialExtensionField<F, PF, D>> for StandardUniform
51where
52    Self: Distribution<PF>,
53{
54    #[inline]
55    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> PackedBinomialExtensionField<F, PF, D> {
56        PackedBinomialExtensionField::new(array::from_fn(|_| self.sample(rng)))
57    }
58}
59
60impl<F: BinomiallyExtendable<D>, PF: PackedField<Scalar = F>, const D: usize>
61    Algebra<BinomialExtensionField<F, D>> for PackedBinomialExtensionField<F, PF, D>
62{
63}
64
65impl<F: BinomiallyExtendable<D>, PF: PackedField<Scalar = F>, const D: usize> Algebra<PF>
66    for PackedBinomialExtensionField<F, PF, D>
67{
68    #[inline]
69    fn mixed_dot_product<const N: usize>(a: &[Self; N], f: &[PF; N]) -> Self
70    where
71        PF: Dup,
72    {
73        // Output container; each coordinate is filled independently below.
74        let mut result = Self::default();
75
76        // One base-field dot product per output coordinate.
77        for k in 0..D {
78            // Strided gather of the k-th coordinate from each extension input:
79            //
80            //     coord_k = [ a_0[k], a_1[k], ..., a_{N-1}[k] ]
81            let coord_k: [PF; N] = core::array::from_fn(|i| a[i].value[k]);
82
83            // Base-level dot product.
84            //
85            // - For Monty-31 packings this is the delayed-reduction primitive;
86            // - For other packings it falls back to the eager default and the override is a no-op gain.
87            result.value[k] = PF::dot_product::<N>(&coord_k, f);
88        }
89
90        result
91    }
92}
93
94impl<F, PF, const D: usize> PrimeCharacteristicRing for PackedBinomialExtensionField<F, PF, D>
95where
96    F: BinomiallyExtendable<D>,
97    PF: PackedField<Scalar = F>,
98{
99    type PrimeSubfield = PF::PrimeSubfield;
100
101    const ZERO: Self = Self::new([PF::ZERO; D]);
102
103    const ONE: Self = Self::new(field_to_array(PF::ONE));
104
105    const TWO: Self = Self::new(field_to_array(PF::TWO));
106
107    const NEG_ONE: Self = Self::new(field_to_array(PF::NEG_ONE));
108
109    #[inline]
110    fn from_prime_subfield(val: Self::PrimeSubfield) -> Self {
111        PF::from_prime_subfield(val).into()
112    }
113
114    #[inline]
115    fn from_bool(b: bool) -> Self {
116        PF::from_bool(b).into()
117    }
118
119    #[inline]
120    fn halve(&self) -> Self {
121        Self::new(self.value.map(|x| x.halve()))
122    }
123
124    #[inline(always)]
125    fn square(&self) -> Self {
126        let mut res = Self::default();
127        let w = F::W;
128        binomial_square(&self.value, &mut res.value, w);
129        res
130    }
131
132    #[inline]
133    fn mul_2exp_u64(&self, exp: u64) -> Self {
134        Self::new(self.value.map(|x| x.mul_2exp_u64(exp)))
135    }
136
137    #[inline]
138    fn div_2exp_u64(&self, exp: u64) -> Self {
139        Self::new(self.value.map(|x| x.div_2exp_u64(exp)))
140    }
141
142    #[inline]
143    fn zero_vec(len: usize) -> Vec<Self> {
144        // SAFETY: this is a repr(transparent) wrapper around an array.
145        unsafe { reconstitute_from_base(PF::zero_vec(len * D)) }
146    }
147}
148
149impl<F, PF, const D: usize> BasedVectorSpace<PF> for PackedBinomialExtensionField<F, PF, D>
150where
151    F: BinomiallyExtendable<D>,
152    PF: PackedField<Scalar = F>,
153{
154    const DIMENSION: usize = D;
155
156    #[inline]
157    fn as_basis_coefficients_slice(&self) -> &[PF] {
158        &self.value
159    }
160
161    #[inline]
162    fn from_basis_coefficients_fn<Fn: FnMut(usize) -> PF>(f: Fn) -> Self {
163        Self::new(array::from_fn(f))
164    }
165
166    #[inline]
167    fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = PF>>(mut iter: I) -> Option<Self> {
168        (iter.len() == D).then(|| Self::new(array::from_fn(|_| iter.next().unwrap()))) // The unwrap is safe as we just checked the length of iter.
169    }
170
171    #[inline]
172    fn flatten_to_base(vec: Vec<Self>) -> Vec<PF> {
173        unsafe {
174            // Safety:
175            // As `Self` is a `repr(transparent)`, it is stored identically in memory to `[PF; D]`
176            flatten_to_base(vec)
177        }
178    }
179
180    #[inline]
181    fn reconstitute_from_base(vec: Vec<PF>) -> Vec<Self> {
182        unsafe {
183            // Safety:
184            // As `Self` is a `repr(transparent)`, it is stored identically in memory to `[PF; D]`
185            reconstitute_from_base(vec)
186        }
187    }
188}
189
190impl<F, const D: usize> PackedFieldExtension<F, BinomialExtensionField<F, D>>
191    for PackedBinomialExtensionField<F, F::Packing, D>
192where
193    F: BinomiallyExtendable<D>,
194{
195    #[inline]
196    fn from_ext_fn(f: impl Fn(usize) -> BinomialExtensionField<F, D>) -> Self {
197        Self::new(F::Packing::pack_columns_fn(|lane| f(lane).value))
198    }
199
200    #[inline]
201    fn add_assign_lane(&mut self, lane: usize, value: BinomialExtensionField<F, D>) {
202        // Add each coefficient into its own base packing at the shared lane.
203        for (coeff, v) in self.value.iter_mut().zip(value.value) {
204            coeff.as_slice_mut()[lane] += v;
205        }
206    }
207
208    #[inline]
209    fn packed_ext_powers(base: BinomialExtensionField<F, D>) -> crate::Powers<Self> {
210        let width = F::Packing::WIDTH;
211        let powers = base.powers().collect_n(width + 1);
212        // Transpose first WIDTH powers
213        let current = Self::from_ext_slice(&powers[..width]);
214
215        // Broadcast self^WIDTH
216        let multiplier = powers[width].into();
217
218        Powers {
219            base: multiplier,
220            current,
221        }
222    }
223}
224
225impl<F, PF, const D: usize> Neg for PackedBinomialExtensionField<F, PF, D>
226where
227    F: BinomiallyExtendable<D>,
228    PF: PackedField<Scalar = F>,
229{
230    type Output = Self;
231
232    #[inline]
233    fn neg(self) -> Self {
234        Self::new(self.value.map(PF::neg))
235    }
236}
237
238impl<F, PF, const D: usize> Add for PackedBinomialExtensionField<F, PF, D>
239where
240    F: BinomiallyExtendable<D>,
241    PF: PackedField<Scalar = F>,
242{
243    type Output = Self;
244
245    #[inline]
246    fn add(self, rhs: Self) -> Self {
247        let value = vector_add(&self.value, &rhs.value);
248        Self::new(value)
249    }
250}
251
252impl<F, PF, const D: usize> Add<BinomialExtensionField<F, D>>
253    for PackedBinomialExtensionField<F, PF, D>
254where
255    F: BinomiallyExtendable<D>,
256    PF: PackedField<Scalar = F>,
257{
258    type Output = Self;
259
260    #[inline]
261    fn add(self, rhs: BinomialExtensionField<F, D>) -> Self {
262        let value = vector_add(&self.value, &rhs.value);
263        Self::new(value)
264    }
265}
266
267impl<F, PF, const D: usize> Add<PF> for PackedBinomialExtensionField<F, PF, D>
268where
269    F: BinomiallyExtendable<D>,
270    PF: PackedField<Scalar = F>,
271{
272    type Output = Self;
273
274    #[inline]
275    fn add(mut self, rhs: PF) -> Self {
276        self.value[0] += rhs;
277        self
278    }
279}
280
281impl<F, PF, const D: usize> AddAssign for PackedBinomialExtensionField<F, PF, D>
282where
283    F: BinomiallyExtendable<D>,
284    PF: PackedField<Scalar = F>,
285{
286    #[inline]
287    fn add_assign(&mut self, rhs: Self) {
288        for i in 0..D {
289            self.value[i] += rhs.value[i];
290        }
291    }
292}
293
294impl<F, PF, const D: usize> AddAssign<BinomialExtensionField<F, D>>
295    for PackedBinomialExtensionField<F, PF, D>
296where
297    F: BinomiallyExtendable<D>,
298    PF: PackedField<Scalar = F>,
299{
300    #[inline]
301    fn add_assign(&mut self, rhs: BinomialExtensionField<F, D>) {
302        for i in 0..D {
303            self.value[i] += rhs.value[i];
304        }
305    }
306}
307
308impl<F, PF, const D: usize> AddAssign<PF> for PackedBinomialExtensionField<F, PF, D>
309where
310    F: BinomiallyExtendable<D>,
311    PF: PackedField<Scalar = F>,
312{
313    #[inline]
314    fn add_assign(&mut self, rhs: PF) {
315        self.value[0] += rhs;
316    }
317}
318
319impl<F, PF, const D: usize> Sum for PackedBinomialExtensionField<F, PF, D>
320where
321    F: BinomiallyExtendable<D>,
322    PF: PackedField<Scalar = F>,
323{
324    #[inline]
325    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
326        iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
327    }
328}
329
330impl<F, PF, const D: usize> Sub for PackedBinomialExtensionField<F, PF, D>
331where
332    F: BinomiallyExtendable<D>,
333    PF: PackedField<Scalar = F>,
334{
335    type Output = Self;
336
337    #[inline]
338    fn sub(self, rhs: Self) -> Self {
339        let value = vector_sub(&self.value, &rhs.value);
340        Self::new(value)
341    }
342}
343
344impl<F, PF, const D: usize> Sub<BinomialExtensionField<F, D>>
345    for PackedBinomialExtensionField<F, PF, D>
346where
347    F: BinomiallyExtendable<D>,
348    PF: PackedField<Scalar = F>,
349{
350    type Output = Self;
351
352    #[inline]
353    fn sub(self, rhs: BinomialExtensionField<F, D>) -> Self {
354        let value = vector_sub(&self.value, &rhs.value);
355        Self::new(value)
356    }
357}
358
359impl<F, PF, const D: usize> Sub<PF> for PackedBinomialExtensionField<F, PF, D>
360where
361    F: BinomiallyExtendable<D>,
362    PF: PackedField<Scalar = F>,
363{
364    type Output = Self;
365
366    #[inline]
367    fn sub(self, rhs: PF) -> Self {
368        let mut res = self.value;
369        res[0] -= rhs;
370        Self::new(res)
371    }
372}
373
374impl<F, PF, const D: usize> SubAssign for PackedBinomialExtensionField<F, PF, D>
375where
376    F: BinomiallyExtendable<D>,
377    PF: PackedField<Scalar = F>,
378{
379    #[inline]
380    fn sub_assign(&mut self, rhs: Self) {
381        *self = *self - rhs;
382    }
383}
384
385impl<F, PF, const D: usize> SubAssign<BinomialExtensionField<F, D>>
386    for PackedBinomialExtensionField<F, PF, D>
387where
388    F: BinomiallyExtendable<D>,
389    PF: PackedField<Scalar = F>,
390{
391    #[inline]
392    fn sub_assign(&mut self, rhs: BinomialExtensionField<F, D>) {
393        *self = *self - rhs;
394    }
395}
396
397impl<F, PF, const D: usize> SubAssign<PF> for PackedBinomialExtensionField<F, PF, D>
398where
399    F: BinomiallyExtendable<D>,
400    PF: PackedField<Scalar = F>,
401{
402    #[inline]
403    fn sub_assign(&mut self, rhs: PF) {
404        *self = *self - rhs;
405    }
406}
407
408impl<F, PF, const D: usize> Mul for PackedBinomialExtensionField<F, PF, D>
409where
410    F: BinomiallyExtendable<D>,
411    PF: PackedField<Scalar = F>,
412{
413    type Output = Self;
414
415    #[inline]
416    fn mul(self, rhs: Self) -> Self {
417        let a = self.value;
418        let b = rhs.value;
419        let mut res = Self::default();
420        let w = F::W;
421
422        binomial_mul::<F, PF, PF, D>(&a, &b, &mut res.value, w);
423
424        res
425    }
426}
427
428impl<F, PF, const D: usize> Mul<BinomialExtensionField<F, D>>
429    for PackedBinomialExtensionField<F, PF, D>
430where
431    F: BinomiallyExtendable<D>,
432    PF: PackedField<Scalar = F>,
433{
434    type Output = Self;
435
436    #[inline]
437    fn mul(self, rhs: BinomialExtensionField<F, D>) -> Self {
438        let a = self.value;
439        let b = rhs.value;
440        let mut res = Self::default();
441        let w = F::W;
442
443        binomial_mul(&a, &b, &mut res.value, w);
444
445        res
446    }
447}
448
449impl<F, PF, const D: usize> Mul<PF> for PackedBinomialExtensionField<F, PF, D>
450where
451    F: BinomiallyExtendable<D>,
452    PF: PackedField<Scalar = F>,
453{
454    type Output = Self;
455
456    #[inline]
457    fn mul(self, rhs: PF) -> Self {
458        Self::new(self.value.map(|x| x * rhs))
459    }
460}
461
462impl<F, PF, const D: usize> Product for PackedBinomialExtensionField<F, PF, D>
463where
464    F: BinomiallyExtendable<D>,
465    PF: PackedField<Scalar = F>,
466{
467    #[inline]
468    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
469        iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
470    }
471}
472
473impl<F, PF, const D: usize> MulAssign for PackedBinomialExtensionField<F, PF, D>
474where
475    F: BinomiallyExtendable<D>,
476    PF: PackedField<Scalar = F>,
477{
478    #[inline]
479    fn mul_assign(&mut self, rhs: Self) {
480        *self = *self * rhs;
481    }
482}
483
484impl<F, PF, const D: usize> MulAssign<BinomialExtensionField<F, D>>
485    for PackedBinomialExtensionField<F, PF, D>
486where
487    F: BinomiallyExtendable<D>,
488    PF: PackedField<Scalar = F>,
489{
490    #[inline]
491    fn mul_assign(&mut self, rhs: BinomialExtensionField<F, D>) {
492        *self = *self * rhs;
493    }
494}
495
496impl<F, PF, const D: usize> MulAssign<PF> for PackedBinomialExtensionField<F, PF, D>
497where
498    F: BinomiallyExtendable<D>,
499    PF: PackedField<Scalar = F>,
500{
501    #[inline]
502    fn mul_assign(&mut self, rhs: PF) {
503        *self = *self * rhs;
504    }
505}
506
507impl<F, PF, const D: usize> Div<BinomialExtensionField<F, D>>
508    for PackedBinomialExtensionField<F, PF, D>
509where
510    F: BinomiallyExtendable<D>,
511    PF: PackedField<Scalar = F>,
512{
513    type Output = Self;
514
515    #[allow(clippy::suspicious_arithmetic_impl)]
516    #[inline]
517    fn div(self, rhs: BinomialExtensionField<F, D>) -> Self {
518        self * Self::from(rhs.inverse())
519    }
520}
521
522impl<F, PF, const D: usize> DivAssign<BinomialExtensionField<F, D>>
523    for PackedBinomialExtensionField<F, PF, D>
524where
525    F: BinomiallyExtendable<D>,
526    PF: PackedField<Scalar = F>,
527{
528    #[inline]
529    fn div_assign(&mut self, rhs: BinomialExtensionField<F, D>) {
530        *self = *self / rhs;
531    }
532}
533
534impl<F: BinomiallyExtendable<D>, const D: usize> Div
535    for PackedBinomialExtensionField<F, F::Packing, D>
536{
537    type Output = Self;
538
539    #[allow(clippy::suspicious_arithmetic_impl)]
540    #[inline]
541    fn div(self, rhs: Self) -> Self {
542        self * crate::invert_packed_extension::<F, BinomialExtensionField<F, D>>(rhs)
543    }
544}
545
546impl<F: BinomiallyExtendable<D>, const D: usize> DivAssign
547    for PackedBinomialExtensionField<F, F::Packing, D>
548{
549    #[inline]
550    fn div_assign(&mut self, rhs: Self) {
551        *self = *self / rhs;
552    }
553}