Skip to main content

p3_dft/
traits.rs

1use alloc::vec::Vec;
2
3use p3_field::{BasedVectorSpace, TwoAdicField};
4use p3_matrix::Matrix;
5use p3_matrix::bitrev::{BitReversedMatrixView, BitReversibleMatrix};
6use p3_matrix::dense::{RowMajorMatrix, RowMajorMatrixView, RowMajorMatrixViewMut};
7use p3_matrix::util::swap_rows;
8
9use crate::util::{coset_shift_cols, divide_by_height};
10
11/// This trait gives an interface for computing discrete fourier transforms (DFT's) and their inverses over
12/// cosets of two-adic subgroups of a field `F`. It also contains combined methods which allow you to take the
13/// evaluation vector of a polynomial on a coset `gH` and extend it to a coset `g'K` for some possibly larger
14/// subgroup `K` and different shift `g'`.
15///
16/// It supports polynomials with evaluations/coefficients valued in either `F` or `A` where `A`
17/// is a vector space over `F` with specified basis. This latter case makes use of the fact that the DFT
18/// is linear meaning we can decompose an `A` valued polynomial into a collection of `F` valued polynomials,
19/// apply the DFT to each of them, and then recombine. When `A` is an extension field, this approach
20/// is much faster than using a `TwoAdicSubgroupDft<A>` implementation directly.
21///
22/// Most implementations of this trait are optimised for the batch case where the input
23/// is a matrix and we is a want to perform the same operation on every column. Note that
24/// depending on the width and height of the matrix (as well as whether or not you are using the
25/// parallel feature) different implementation may be faster. Hence depending on your use case
26/// you may want to be using `Radix2Dit`, `Radix2DitParallel`, `Radix2DFTSmallBatch` or
27/// `Radix2Bowers` (or, for `MontyField31` fields, `p3_monty_31::RecursiveDft`).
28pub trait TwoAdicSubgroupDft<F: TwoAdicField>: Clone + Default {
29    /// The matrix type used to store the result of a batched DFT operation.
30    ///
31    /// This type represents a matrix of field elements, used to hold the evaluations
32    /// of multiple polynomials over a two-adic subgroup or its coset.
33    /// It is always owned and supports efficient access and transformation
34    /// patterns used in FFT-based algorithms.
35    ///
36    /// Most implementations use `RowMajorMatrix<F>` or a wrapper like
37    /// `BitReversedMatrixView<RowMajorMatrix<F>>` to allow in-place bit-reversed access.
38    type Evaluations: BitReversibleMatrix<F> + 'static;
39
40    /// Compute the discrete Fourier transform (DFT) of `vec`.
41    ///
42    /// #### Mathematical Description
43    ///
44    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
45    /// Treating `vec` as coefficients of a polynomial, compute the evaluations
46    /// of that polynomial on the subgroup `H`.
47    fn dft(&self, vec: Vec<F>) -> Vec<F> {
48        self.dft_batch(RowMajorMatrix::new_col(vec))
49            .to_row_major_matrix()
50            .values
51    }
52
53    /// Compute the discrete Fourier transform (DFT) of each column in `mat`.
54    /// This is the only method an implementer needs to define, all other
55    /// methods can be derived from this one.
56    ///
57    /// #### Mathematical Description
58    ///
59    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
60    /// Treating each column of `mat` as the coefficients of a polynomial, compute the
61    /// evaluations of those polynomials on the subgroup `H`.
62    fn dft_batch(&self, mat: RowMajorMatrix<F>) -> Self::Evaluations;
63
64    /// Compute the "coset DFT" of `vec`.
65    ///
66    /// #### Mathematical Description
67    ///
68    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
69    /// Treating `vec` as coefficients of a polynomial, compute the evaluations
70    /// of that polynomial on the coset `shift * H`.
71    fn coset_dft(&self, vec: Vec<F>, shift: F) -> Vec<F> {
72        self.coset_dft_batch(RowMajorMatrix::new_col(vec), shift)
73            .to_row_major_matrix()
74            .values
75    }
76
77    /// Compute the "coset DFT" of each column in `mat`.
78    ///
79    /// #### Mathematical Description
80    ///
81    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
82    /// Treating each column of `mat` as the coefficients of a polynomial, compute the
83    /// evaluations of those polynomials on the coset `shift * H`.
84    fn coset_dft_batch(&self, mut mat: RowMajorMatrix<F>, shift: F) -> Self::Evaluations {
85        // Observe that
86        //     y_i = \sum_j c_j (s g^i)^j
87        //         = \sum_j (c_j s^j) (g^i)^j
88        // which has the structure of an ordinary DFT, except each coefficient `c_j` is first replaced
89        // by `c_j s^j`.
90        coset_shift_cols(&mut mat, shift);
91        self.dft_batch(mat)
92    }
93
94    /// Compute the inverse DFT of `vec`.
95    ///
96    /// #### Mathematical Description
97    ///
98    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
99    /// Treating `vec` as the evaluations of a polynomial on `H`, compute the
100    /// coefficients of that polynomial.
101    fn idft(&self, vec: Vec<F>) -> Vec<F> {
102        self.idft_batch(RowMajorMatrix::new_col(vec)).values
103    }
104
105    /// Compute the inverse DFT of each column in `mat`.
106    ///
107    /// #### Mathematical Description
108    ///
109    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
110    /// Treating each column of `mat` as the evaluations of a polynomial on `H`,
111    /// compute the coefficients of those polynomials.
112    fn idft_batch(&self, mat: RowMajorMatrix<F>) -> RowMajorMatrix<F> {
113        let mut dft = self.dft_batch(mat).to_row_major_matrix();
114        let h = dft.height();
115
116        divide_by_height(&mut dft);
117
118        for row in 1..h / 2 {
119            swap_rows(&mut dft, row, h - row);
120        }
121
122        dft
123    }
124
125    /// Compute the "coset iDFT" of `vec`. This is the inverse operation of "coset DFT".
126    ///
127    /// #### Mathematical Description
128    ///
129    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
130    /// Treating `vec` as the evaluations of a polynomial on `shift * H`,
131    /// compute the coefficients of this polynomial.
132    fn coset_idft(&self, vec: Vec<F>, shift: F) -> Vec<F> {
133        self.coset_idft_batch(RowMajorMatrix::new_col(vec), shift)
134            .values
135    }
136
137    /// Compute the "coset iDFT" of each column in `mat`. This is the inverse operation
138    /// of "coset DFT".
139    ///
140    /// #### Mathematical Description
141    ///
142    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
143    /// Treating each column of `mat` as the evaluations of a polynomial on `shift * H`,
144    /// compute the coefficients of those polynomials.
145    fn coset_idft_batch(&self, mut mat: RowMajorMatrix<F>, shift: F) -> RowMajorMatrix<F> {
146        // Let `f(x)` denote the polynomial we want. Then, if we reinterpret the columns
147        // as being over the subgroup `H`, this is equivalent to switching our polynomial
148        // to `g(x) = f(sx)`.
149        // The output of the iDFT is the coefficients of `g` so to get the coefficients of
150        // `f` we need to scale the `i`'th coefficient by `s^{-i}`.
151        mat = self.idft_batch(mat);
152        coset_shift_cols(&mut mat, shift.inverse());
153        mat
154    }
155
156    /// Compute the low-degree extension of `vec` onto a larger subgroup.
157    ///
158    /// #### Mathematical Description
159    ///
160    /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
161    /// and `vec.len() << added_bits`, respectively.
162    /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
163    /// compute the evaluations of that polynomial on the subgroup `K`.
164    ///
165    /// There is another way to interpret this transformation which gives a larger
166    /// use case. We can also view it as treating columns of `mat` as evaluations
167    /// over a coset `gH` and then computing the evaluations of those polynomials
168    /// on the coset `gK`.
169    fn lde(&self, vec: Vec<F>, added_bits: usize) -> Vec<F> {
170        self.lde_batch(RowMajorMatrix::new_col(vec), added_bits)
171            .to_row_major_matrix()
172            .values
173    }
174
175    /// Compute the low-degree extension of each column in `mat` onto a larger subgroup.
176    ///
177    /// #### Mathematical Description
178    ///
179    /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
180    /// and `mat.height() << added_bits`, respectively.
181    /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
182    /// compute the evaluations of those polynomials on the subgroup `K`.
183    ///
184    /// There is another way to interpret this transformation which gives a larger
185    /// use case. We can also view it as treating columns of `mat` as evaluations
186    /// over a coset `gH` and then computing the evaluations of those polynomials
187    /// on the coset `gK`.
188    fn lde_batch(&self, mat: RowMajorMatrix<F>, added_bits: usize) -> Self::Evaluations {
189        // This is a better default as several implementations have a custom implementation
190        // of `coset_lde_batch` and often the fact that the shift is `ONE` won't give any
191        // performance improvements anyway.
192        self.coset_lde_batch(mat, added_bits, F::ONE)
193    }
194
195    /// Compute the low-degree extension of of `vec` onto a coset of a larger subgroup.
196    ///
197    /// #### Mathematical Description
198    ///
199    /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
200    /// and `vec.len() << added_bits`, respectively.
201    /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
202    /// compute the evaluations of that polynomial on the coset `shift * K`.
203    ///
204    /// There is another way to interpret this transformation which gives a larger
205    /// use case. We can also view it as treating `vec` as the evaluations of a polynomial
206    /// over a coset `gH` and then computing the evaluations of that polynomial
207    /// on the coset `g'K` where `g' = g * shift`.
208    fn coset_lde(&self, vec: Vec<F>, added_bits: usize, shift: F) -> Vec<F> {
209        self.coset_lde_batch(RowMajorMatrix::new_col(vec), added_bits, shift)
210            .to_row_major_matrix()
211            .values
212    }
213
214    /// Compute the low-degree extension of each column in `mat` onto a coset of a larger subgroup.
215    ///
216    /// #### Mathematical Description
217    ///
218    /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
219    /// and `mat.height() << added_bits`, respectively.
220    /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
221    /// compute the evaluations of those polynomials on the coset `shift * K`.
222    ///
223    /// There is another way to interpret this transformation which gives a larger
224    /// use case. We can also view it as treating columns of `mat` as evaluations
225    /// over a coset `gH` and then computing the evaluations of those polynomials
226    /// on the coset `g'K` where `g' = g * shift`.
227    fn coset_lde_batch(
228        &self,
229        mat: RowMajorMatrix<F>,
230        added_bits: usize,
231        shift: F,
232    ) -> Self::Evaluations {
233        self.coset_lde_batch_with_transform(mat, added_bits, shift, |_, _| {})
234    }
235
236    /// Like [`coset_lde_batch`](Self::coset_lde_batch), but with a closure
237    /// invoked on the intermediate coefficient buffer between the iDFT and
238    /// the forward DFT phases. The [`Layout`] argument tells the closure
239    /// whether the buffer is in natural or bit-reversed memory order, so the
240    /// closure can translate memory positions to natural-order coefficient
241    /// indices when relevant.
242    fn coset_lde_batch_with_transform<T>(
243        &self,
244        mat: RowMajorMatrix<F>,
245        added_bits: usize,
246        shift: F,
247        transform: T,
248    ) -> Self::Evaluations
249    where
250        T: FnOnce(&mut RowMajorMatrixViewMut<'_, F>, Layout),
251    {
252        let mut coeffs = self.idft_batch(mat);
253        transform(&mut coeffs.as_view_mut(), Layout::Natural);
254        // PANICS: possible panic if the new resized length overflows
255        let scale = 1usize.checked_shl(added_bits.try_into().unwrap()).unwrap();
256        let new_len = coeffs.values.len().checked_mul(scale).unwrap();
257        coeffs.values.resize(new_len, F::ZERO);
258        self.coset_dft_batch(coeffs, shift)
259    }
260
261    /// Like [`coset_lde_batch_with_transform`](Self::coset_lde_batch_with_transform),
262    /// but also visit completed evaluation blocks.
263    ///
264    /// The returned view has natural row order over bit-reversed storage. `make_consumer`
265    /// runs once on the calling thread, before any block is published, with the block height:
266    /// a positive power of two dividing the output height. The returned consumer receives each
267    /// block's starting physical row and a read-only view of exactly that many rows.
268    ///
269    /// On successful return, every output row has been visited exactly once. Calls may
270    /// overlap and arrive in any order. Each view lasts only for its call, and the DFT
271    /// never writes a block after publishing it. All calls finish before this method returns.
272    ///
273    /// `transform` runs once on the calling thread before any consumer call. The default
274    /// implementation publishes the full output as one block; implementations may publish
275    /// smaller blocks as they complete.
276    fn coset_lde_batch_with_blocks<T, K, C>(
277        &self,
278        mat: RowMajorMatrix<F>,
279        added_bits: usize,
280        shift: F,
281        transform: T,
282        make_consumer: K,
283    ) -> BitReversedMatrixView<RowMajorMatrix<F>>
284    where
285        T: FnOnce(&mut RowMajorMatrixViewMut<'_, F>, Layout),
286        K: FnOnce(usize) -> C,
287        C: Fn(usize, RowMajorMatrixView<'_, F>) + Sync,
288    {
289        let output = self
290            .coset_lde_batch_with_transform(mat, added_bits, shift, transform)
291            .bit_reverse_rows()
292            .to_row_major_matrix();
293        let consume = make_consumer(output.height());
294        consume(0, output.as_view());
295        output.bit_reverse_rows()
296    }
297
298    /// Compute the discrete Fourier transform (DFT) of `vec`.
299    ///
300    /// #### Mathematical Description
301    ///
302    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
303    /// Treating `vec` as coefficients of a polynomial, compute the evaluations
304    /// of that polynomial on the subgroup `H`.
305    fn dft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(&self, vec: Vec<V>) -> Vec<V> {
306        self.dft_algebra_batch(RowMajorMatrix::new_col(vec)).values
307    }
308
309    /// Compute the discrete Fourier transform (DFT) of each column in `mat`.
310    ///
311    /// #### Mathematical Description
312    ///
313    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
314    /// Treating each column of `mat` as the coefficients of a polynomial, compute the
315    /// evaluations of those polynomials on the subgroup `H`.
316    fn dft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
317        &self,
318        mat: RowMajorMatrix<V>,
319    ) -> RowMajorMatrix<V> {
320        let init_width = mat.width();
321        let base_mat =
322            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
323        let base_dft_output = self.dft_batch(base_mat).to_row_major_matrix();
324        RowMajorMatrix::new(
325            V::reconstitute_from_base(base_dft_output.values),
326            init_width,
327        )
328    }
329
330    /// Compute the "coset DFT" of `vec`.
331    ///
332    /// #### Mathematical Description
333    ///
334    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
335    /// Treating `vec` as coefficients of a polynomial, compute the evaluations
336    /// of that polynomial on the coset `shift * H`.
337    fn coset_dft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
338        &self,
339        vec: Vec<V>,
340        shift: F,
341    ) -> Vec<V> {
342        self.coset_dft_algebra_batch(RowMajorMatrix::new_col(vec), shift)
343            .to_row_major_matrix()
344            .values
345    }
346
347    /// Compute the "coset DFT" of each column in `mat`.
348    ///
349    /// #### Mathematical Description
350    ///
351    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
352    /// Treating each column of `mat` as the coefficients of a polynomial, compute the
353    /// evaluations of those polynomials on the coset `shift * H`.
354    fn coset_dft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
355        &self,
356        mat: RowMajorMatrix<V>,
357        shift: F,
358    ) -> RowMajorMatrix<V> {
359        let init_width = mat.width();
360        let base_mat =
361            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
362        let base_dft_output = self.coset_dft_batch(base_mat, shift).to_row_major_matrix();
363        RowMajorMatrix::new(
364            V::reconstitute_from_base(base_dft_output.values),
365            init_width,
366        )
367    }
368
369    /// Compute the inverse DFT of `vec`.
370    ///
371    /// #### Mathematical Description
372    ///
373    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
374    /// Treating `vec` as the evaluations of a polynomial on `H`, compute the
375    /// coefficients of that polynomial.
376    fn idft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(&self, vec: Vec<V>) -> Vec<V> {
377        self.idft_algebra_batch(RowMajorMatrix::new_col(vec)).values
378    }
379
380    /// Compute the inverse DFT of each column in `mat`.
381    ///
382    /// #### Mathematical Description
383    ///
384    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
385    /// Treating each column of `mat` as the evaluations of a polynomial on `H`,
386    /// compute the coefficients of those polynomials.
387    fn idft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
388        &self,
389        mat: RowMajorMatrix<V>,
390    ) -> RowMajorMatrix<V> {
391        let init_width = mat.width();
392        let base_mat =
393            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
394        let base_dft_output = self.idft_batch(base_mat);
395        RowMajorMatrix::new(
396            V::reconstitute_from_base(base_dft_output.values),
397            init_width,
398        )
399    }
400
401    /// Compute the "coset iDFT" of `vec`. This is the inverse operation of "coset DFT".
402    ///
403    /// #### Mathematical Description
404    ///
405    /// Let `H` denote the unique multiplicative subgroup of order `vec.len()`.
406    /// Treating `vec` as the evaluations of a polynomial on `shift * H`,
407    /// compute the coefficients of this polynomial.
408    fn coset_idft_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
409        &self,
410        vec: Vec<V>,
411        shift: F,
412    ) -> Vec<V> {
413        self.coset_idft_algebra_batch(RowMajorMatrix::new_col(vec), shift)
414            .values
415    }
416
417    /// Compute the "coset iDFT" of each column in `mat`. This is the inverse operation
418    /// of "coset DFT".
419    ///
420    /// #### Mathematical Description
421    ///
422    /// Let `H` denote the unique multiplicative subgroup of order `mat.height()`.
423    /// Treating each column of `mat` as the evaluations of a polynomial on `shift * H`,
424    /// compute the coefficients of those polynomials.
425    fn coset_idft_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
426        &self,
427        mat: RowMajorMatrix<V>,
428        shift: F,
429    ) -> RowMajorMatrix<V> {
430        let init_width = mat.width();
431        let base_mat =
432            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
433        let base_dft_output = self.coset_idft_batch(base_mat, shift);
434        RowMajorMatrix::new(
435            V::reconstitute_from_base(base_dft_output.values),
436            init_width,
437        )
438    }
439
440    /// Compute the low-degree extension of `vec` onto a larger subgroup.
441    ///
442    /// #### Mathematical Description
443    ///
444    /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
445    /// and `vec.len() << added_bits`, respectively.
446    /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
447    /// compute the evaluations of that polynomial on the subgroup `K`.
448    ///
449    /// There is another way to interpret this transformation which gives a larger
450    /// use case. We can also view it as treating columns of `mat` as evaluations
451    /// over a coset `gH` and then computing the evaluations of those polynomials
452    /// on the coset `gK`.
453    fn lde_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
454        &self,
455        vec: Vec<V>,
456        added_bits: usize,
457    ) -> Vec<V> {
458        self.lde_algebra_batch(RowMajorMatrix::new_col(vec), added_bits)
459            .to_row_major_matrix()
460            .values
461    }
462
463    /// Compute the low-degree extension of each column in `mat` onto a larger subgroup.
464    ///
465    /// #### Mathematical Description
466    ///
467    /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
468    /// and `mat.height() << added_bits`, respectively.
469    /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
470    /// compute the evaluations of those polynomials on the subgroup `K`.
471    ///
472    /// There is another way to interpret this transformation which gives a larger
473    /// use case. We can also view it as treating columns of `mat` as evaluations
474    /// over a coset `gH` and then computing the evaluations of those polynomials
475    /// on the coset `gK`.
476    fn lde_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
477        &self,
478        mat: RowMajorMatrix<V>,
479        added_bits: usize,
480    ) -> RowMajorMatrix<V> {
481        let init_width = mat.width();
482        let base_mat =
483            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
484        let base_dft_output = self.lde_batch(base_mat, added_bits).to_row_major_matrix();
485        RowMajorMatrix::new(
486            V::reconstitute_from_base(base_dft_output.values),
487            init_width,
488        )
489    }
490
491    /// Compute the low-degree extension of of `vec` onto a coset of a larger subgroup.
492    ///
493    /// #### Mathematical Description
494    ///
495    /// Let `H, K` denote the unique multiplicative subgroups of order `vec.len()`
496    /// and `vec.len() << added_bits`, respectively.
497    /// Treating `vec` as the evaluations of a polynomial on the subgroup `H`,
498    /// compute the evaluations of that polynomial on the coset `shift * K`.
499    ///
500    /// There is another way to interpret this transformation which gives a larger
501    /// use case. We can also view it as treating `vec` as the evaluations of a polynomial
502    /// over a coset `gH` and then computing the evaluations of that polynomial
503    /// on the coset `g'K` where `g' = g * shift`.
504    fn coset_lde_algebra<V: BasedVectorSpace<F> + Clone + Send + Sync>(
505        &self,
506        vec: Vec<V>,
507        added_bits: usize,
508        shift: F,
509    ) -> Vec<V> {
510        self.coset_lde_algebra_batch(RowMajorMatrix::new_col(vec), added_bits, shift)
511            .to_row_major_matrix()
512            .values
513    }
514
515    /// Compute the low-degree extension of each column in `mat` onto a coset of a larger subgroup.
516    ///
517    /// #### Mathematical Description
518    ///
519    /// Let `H, K` denote the unique multiplicative subgroups of order `mat.height()`
520    /// and `mat.height() << added_bits`, respectively.
521    /// Treating each column of `mat` as the evaluations of a polynomial on the subgroup `H`,
522    /// compute the evaluations of those polynomials on the coset `shift * K`.
523    ///
524    /// There is another way to interpret this transformation which gives a larger
525    /// use case. We can also view it as treating columns of `mat` as evaluations
526    /// over a coset `gH` and then computing the evaluations of those polynomials
527    /// on the coset `g'K` where `g' = g * shift`.
528    fn coset_lde_algebra_batch<V: BasedVectorSpace<F> + Clone + Send + Sync>(
529        &self,
530        mat: RowMajorMatrix<V>,
531        added_bits: usize,
532        shift: F,
533    ) -> RowMajorMatrix<V> {
534        let init_width = mat.width();
535        let base_mat =
536            RowMajorMatrix::new(V::flatten_to_base(mat.values), init_width * V::DIMENSION);
537        let base_dft_output = self
538            .coset_lde_batch(base_mat, added_bits, shift)
539            .to_row_major_matrix();
540        RowMajorMatrix::new(
541            V::reconstitute_from_base(base_dft_output.values),
542            init_width,
543        )
544    }
545}
546
547/// Memory layout of the coefficient buffer passed to a transform closure in
548/// [`TwoAdicSubgroupDft::coset_lde_batch_with_transform`].
549#[derive(Copy, Clone, Debug, PartialEq, Eq)]
550pub enum Layout {
551    /// Memory row `m` corresponds to natural-order index `m`.
552    Natural,
553    /// Memory row `m` corresponds to natural-order index
554    /// `reverse_bits_len(m, log2_strict_usize(buf.height()))`.
555    BitReversed,
556}