extern crate alloc;
use crate::field::{element::FieldElement, fields::mersenne31::field::Mersenne31Field};
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
pub fn cfft(
input: &mut [FieldElement<Mersenne31Field>],
twiddles: Vec<Vec<FieldElement<Mersenne31Field>>>,
) {
let log_2_size = input.len().trailing_zeros();
(0..log_2_size).for_each(|i| {
let chunk_size = 1 << (i + 1);
let half_chunk_size = 1 << i;
input.chunks_mut(chunk_size).for_each(|chunk| {
let (hi_part, low_part) = chunk.split_at_mut(half_chunk_size);
hi_part
.iter_mut()
.zip(low_part)
.enumerate()
.for_each(|(j, (hi, low))| {
let temp = *low * twiddles[i as usize][j];
*low = *hi - temp;
*hi += temp
});
});
});
}
#[cfg(feature = "alloc")]
pub fn icfft(
input: &mut [FieldElement<Mersenne31Field>],
twiddles: Vec<Vec<FieldElement<Mersenne31Field>>>,
) {
let log_2_size = input.len().trailing_zeros();
(0..log_2_size).for_each(|i| {
let chunk_size = 1 << (log_2_size - i);
let half_chunk_size = chunk_size >> 1;
input.chunks_mut(chunk_size).for_each(|chunk| {
let (hi_part, low_part) = chunk.split_at_mut(half_chunk_size);
hi_part
.iter_mut()
.zip(low_part)
.enumerate()
.for_each(|(j, (hi, low))| {
let temp = *hi + *low;
*low = (*hi - *low) * twiddles[i as usize][j];
*hi = temp;
});
});
});
}
pub fn order_cfft_result_naive(
input: &[FieldElement<Mersenne31Field>],
) -> Vec<FieldElement<Mersenne31Field>> {
let mut result = Vec::new();
let length = input.len();
for i in 0..length / 2 {
result.push(input[i]); result.push(input[length - i - 1]); }
result
}
pub fn order_icfft_input_naive(
input: &mut [FieldElement<Mersenne31Field>],
) -> Vec<FieldElement<Mersenne31Field>> {
let mut result = Vec::new();
(0..input.len()).step_by(2).for_each(|i| {
result.push(input[i]);
});
(1..input.len()).step_by(2).rev().for_each(|i| {
result.push(input[i]);
});
result
}
#[cfg(test)]
mod tests {
use super::*;
type FE = FieldElement<Mersenne31Field>;
#[test]
fn ordering_cfft_result_works_for_4_points() {
let expected_slice = [FE::from(0), FE::from(1), FE::from(2), FE::from(3)];
let slice = [FE::from(0), FE::from(2), FE::from(3), FE::from(1)];
let res = order_cfft_result_naive(&slice);
assert_eq!(res, expected_slice)
}
#[test]
fn ordering_cfft_result_works_for_16_points() {
let expected_slice = [
FE::from(0),
FE::from(1),
FE::from(2),
FE::from(3),
FE::from(4),
FE::from(5),
FE::from(6),
FE::from(7),
FE::from(8),
FE::from(9),
FE::from(10),
FE::from(11),
FE::from(12),
FE::from(13),
FE::from(14),
FE::from(15),
];
let slice = [
FE::from(0),
FE::from(2),
FE::from(4),
FE::from(6),
FE::from(8),
FE::from(10),
FE::from(12),
FE::from(14),
FE::from(15),
FE::from(13),
FE::from(11),
FE::from(9),
FE::from(7),
FE::from(5),
FE::from(3),
FE::from(1),
];
let res = order_cfft_result_naive(&slice);
assert_eq!(res, expected_slice)
}
#[test]
fn from_natural_to_icfft_input_order_works() {
let mut slice = [
FE::from(0),
FE::from(1),
FE::from(2),
FE::from(3),
FE::from(4),
FE::from(5),
FE::from(6),
FE::from(7),
FE::from(8),
FE::from(9),
FE::from(10),
FE::from(11),
FE::from(12),
FE::from(13),
FE::from(14),
FE::from(15),
];
let expected_slice = [
FE::from(0),
FE::from(2),
FE::from(4),
FE::from(6),
FE::from(8),
FE::from(10),
FE::from(12),
FE::from(14),
FE::from(15),
FE::from(13),
FE::from(11),
FE::from(9),
FE::from(7),
FE::from(5),
FE::from(3),
FE::from(1),
];
let res = order_icfft_input_naive(&mut slice);
assert_eq!(res, expected_slice)
}
}