use super::{permute_index, FieldElement};
const MAX_LOOP: usize = 256;
#[allow(clippy::len_without_is_empty)]
pub trait FftInputs<E: FieldElement> {
fn len(&self) -> usize;
fn butterfly(&mut self, offset: usize, stride: usize);
fn butterfly_twiddle(&mut self, twiddle: E::BaseField, offset: usize, stride: usize);
fn swap(&mut self, i: usize, j: usize);
fn shift_by_series(&mut self, offset: E::BaseField, increment: E::BaseField);
fn shift_by(&mut self, offset: E::BaseField);
fn permute(&mut self) {
let n = self.len();
for i in 0..n {
let j = permute_index(n, i);
if j > i {
self.swap(i, j);
}
}
}
fn fft_in_place(&mut self, twiddles: &[E::BaseField]) {
fft_in_place(self, twiddles, 1, 1, 0);
}
fn fft_in_place_raw(
&mut self,
twiddles: &[E::BaseField],
count: usize,
stride: usize,
offset: usize,
) {
fft_in_place(self, twiddles, count, stride, offset)
}
}
impl<E: FieldElement> FftInputs<E> for [E] {
fn len(&self) -> usize {
self.len()
}
#[inline(always)]
fn butterfly(&mut self, offset: usize, stride: usize) {
let i = offset;
let j = offset + stride;
let temp = self[i];
self[i] = temp + self[j];
self[j] = temp - self[j];
}
#[inline(always)]
fn butterfly_twiddle(&mut self, twiddle: E::BaseField, offset: usize, stride: usize) {
let i = offset;
let j = offset + stride;
let temp = self[i];
self[j] = self[j].mul_base(twiddle);
self[i] = temp + self[j];
self[j] = temp - self[j];
}
fn swap(&mut self, i: usize, j: usize) {
self.swap(i, j)
}
fn shift_by_series(&mut self, offset: E::BaseField, increment: E::BaseField) {
let mut offset = E::from(offset);
let increment = E::from(increment);
for d in self.iter_mut() {
*d *= offset;
offset *= increment;
}
}
fn shift_by(&mut self, offset: E::BaseField) {
let offset = E::from(offset);
for d in self.iter_mut() {
*d *= offset;
}
}
}
#[allow(clippy::needless_range_loop)]
impl<E: FieldElement, const N: usize> FftInputs<E> for [[E; N]] {
fn len(&self) -> usize {
self.len()
}
#[inline(always)]
fn butterfly(&mut self, offset: usize, stride: usize) {
let i = offset;
let j = offset + stride;
let temp = self[i];
for col_idx in 0..N {
self[i][col_idx] = temp[col_idx] + self[j][col_idx];
self[j][col_idx] = temp[col_idx] - self[j][col_idx];
}
}
#[inline(always)]
fn butterfly_twiddle(&mut self, twiddle: E::BaseField, offset: usize, stride: usize) {
let i = offset;
let j = offset + stride;
let twiddle = E::from(twiddle);
let temp = self[i];
for col_idx in 0..N {
self[j][col_idx] *= twiddle;
self[i][col_idx] = temp[col_idx] + self[j][col_idx];
self[j][col_idx] = temp[col_idx] - self[j][col_idx];
}
}
fn swap(&mut self, i: usize, j: usize) {
self.swap(i, j)
}
fn shift_by(&mut self, offset: E::BaseField) {
let offset = E::from(offset);
for row_idx in 0..self.len() {
for col_idx in 0..N {
self[row_idx][col_idx] *= offset;
}
}
}
fn shift_by_series(&mut self, offset: E::BaseField, increment: E::BaseField) {
let increment = E::from(increment);
let mut offset = E::from(offset);
for row_idx in 0..self.len() {
for col_idx in 0..N {
self[row_idx][col_idx] *= offset;
}
offset *= increment;
}
}
}
fn fft_in_place<E, I>(
values: &mut I,
twiddles: &[E::BaseField],
count: usize,
stride: usize,
offset: usize,
) where
E: FieldElement,
I: FftInputs<E> + ?Sized,
{
let size = values.len() / stride;
debug_assert!(size.is_power_of_two());
debug_assert!(offset < stride);
debug_assert_eq!(values.len() % size, 0);
if size > 2 {
if stride == count && count < MAX_LOOP {
fft_in_place(values, twiddles, 2 * count, 2 * stride, offset);
} else {
fft_in_place(values, twiddles, count, 2 * stride, offset);
fft_in_place(values, twiddles, count, 2 * stride, offset + stride);
}
}
for offset in offset..(offset + count) {
I::butterfly(values, offset, stride);
}
let last_offset = offset + size * stride;
for (i, offset) in (offset..last_offset)
.step_by(2 * stride)
.enumerate()
.skip(1)
{
for j in offset..(offset + count) {
I::butterfly_twiddle(values, twiddles[i], j, stride);
}
}
}