#![allow(clippy::use_self)]
extern crate alloc;
use alloc::vec::Vec;
use itertools::izip;
use p3_field::{Field, PackedFieldPow2, PackedValue, PrimeCharacteristicRing, TwoAdicField};
use p3_util::log2_strict_usize;
use crate::utils::monty_reduce;
use crate::{FieldParameters, MontyField31, TwoAdicData};
impl<MP: FieldParameters + TwoAdicData> MontyField31<MP> {
pub fn roots_of_unity_table(n: usize) -> Vec<Vec<Self>> {
let lg_n = log2_strict_usize(n);
let generator = Self::two_adic_generator(lg_n);
let half_n = 1 << (lg_n - 1);
let nth_roots = generator.powers().collect_n(half_n);
(0..(lg_n - 1))
.map(|i| nth_roots.iter().step_by(1 << i).copied().collect())
.rev()
.collect()
}
pub fn get_missing_twiddles(req_lg_n: usize, cur_lg_n: usize) -> Vec<Vec<Self>> {
let main_generator = Self::two_adic_generator(req_lg_n);
(cur_lg_n..req_lg_n)
.map(|level| {
let count = 1 << level;
let sub_generator_exp = 1 << (req_lg_n - level - 1);
let sub_generator = main_generator.exp_u64(sub_generator_exp as u64);
sub_generator.powers().collect_n(count)
})
.collect()
}
}
#[inline(always)]
fn forward_butterfly<T: PrimeCharacteristicRing + Copy>(x: T, y: T, roots: T) -> (T, T) {
let t = x - y;
(x + y, t * roots)
}
#[inline(always)]
fn monty_forward_butterfly<MP: FieldParameters + TwoAdicData>(
x: <MontyField31<MP> as Field>::Packing,
y: <MontyField31<MP> as Field>::Packing,
roots: <MontyField31<MP> as Field>::Packing,
) -> (
<MontyField31<MP> as Field>::Packing,
<MontyField31<MP> as Field>::Packing,
) {
#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
{
x.forward_butterfly(y, roots)
}
#[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
{
forward_butterfly(x, y, roots)
}
}
#[inline(always)]
fn forward_butterfly_interleaved<const HALF_RADIX: usize, T: PackedFieldPow2>(
x: T,
y: T,
roots: T,
) -> (T, T) {
let (x, y) = x.interleave(y, HALF_RADIX);
let (x, y) = forward_butterfly(x, y, roots);
x.interleave(y, HALF_RADIX)
}
#[inline]
fn forward_iterative_packed<const HALF_RADIX: usize, T: PackedFieldPow2>(
input: &mut [T],
roots: &[T::Scalar],
) {
let roots = T::from_fn(|i| roots[i % HALF_RADIX]);
input.chunks_exact_mut(2).for_each(|pair| {
let (x, y) = forward_butterfly_interleaved::<HALF_RADIX, _>(pair[0], pair[1], roots);
pair[0] = x;
pair[1] = y;
});
}
#[inline]
fn forward_iterative_packed_radix_2<T: PackedFieldPow2>(input: &mut [T]) {
input.chunks_exact_mut(2).for_each(|pair| {
let x = pair[0];
let y = pair[1];
let (mut x, y) = x.interleave(y, 1);
let t = x - y; x += y;
let (x, y) = x.interleave(t, 1);
pair[0] = x;
pair[1] = y;
});
}
impl<MP: FieldParameters + TwoAdicData> MontyField31<MP> {
#[inline]
fn forward_iterative_layer(
packed_input: &mut [<Self as Field>::Packing],
roots: &[Self],
m: usize,
) {
debug_assert_eq!(roots.len(), m);
let packed_roots = <Self as Field>::Packing::pack_slice(roots);
let packed_m = m / <Self as Field>::Packing::WIDTH;
packed_input
.chunks_exact_mut(2 * packed_m)
.for_each(|layer_chunk| {
let (xs, ys) = unsafe { layer_chunk.split_at_mut_unchecked(packed_m) };
izip!(xs, ys, packed_roots)
.for_each(|(x, y, &root)| (*x, *y) = monty_forward_butterfly(*x, *y, root));
});
}
#[inline]
fn monty_forward_pass_packed(input: &mut [<Self as Field>::Packing], roots: &[Self]) {
let packed_roots = <Self as Field>::Packing::pack_slice(roots);
let n = input.len();
let (xs, ys) = unsafe { input.split_at_mut_unchecked(n / 2) };
izip!(xs, ys, packed_roots)
.for_each(|(x, y, &roots)| (*x, *y) = monty_forward_butterfly(*x, *y, roots));
}
#[inline]
fn monty_forward_iterative_layer_1(input: &mut [<Self as Field>::Packing], roots: &[Self]) {
let packed_roots = <Self as Field>::Packing::pack_slice(roots);
let n = input.len();
let (top_half, bottom_half) = unsafe { input.split_at_mut_unchecked(n / 2) };
let (xs, ys) = unsafe { top_half.split_at_mut_unchecked(n / 4) };
let (zs, ws) = unsafe { bottom_half.split_at_mut_unchecked(n / 4) };
izip!(xs, ys, zs, ws, packed_roots).for_each(|(x, y, z, w, &root)| {
(*x, *y) = monty_forward_butterfly(*x, *y, root);
(*z, *w) = monty_forward_butterfly(*z, *w, root);
});
}
#[inline]
fn forward_iterative_packed_radix_16(input: &mut [<Self as Field>::Packing]) {
if <Self as Field>::Packing::WIDTH >= 16 {
forward_iterative_packed::<8, _>(input, MP::ROOTS_16.as_ref());
} else {
Self::forward_iterative_layer(input, MP::ROOTS_16.as_ref(), 8);
}
if <Self as Field>::Packing::WIDTH >= 8 {
forward_iterative_packed::<4, _>(input, MP::ROOTS_8.as_ref());
} else {
Self::forward_iterative_layer(input, MP::ROOTS_8.as_ref(), 4);
}
let roots4 = [MP::ROOTS_8.as_ref()[0], MP::ROOTS_8.as_ref()[2]];
if <Self as Field>::Packing::WIDTH >= 4 {
forward_iterative_packed::<2, _>(input, &roots4);
} else {
Self::forward_iterative_layer(input, &roots4, 2);
}
forward_iterative_packed_radix_2(input);
}
#[inline]
fn forward_iterative(packed_input: &mut [<Self as Field>::Packing], root_table: &[Vec<Self>]) {
assert!(packed_input.len() >= 2);
let packing_width = <Self as Field>::Packing::WIDTH;
let n = packed_input.len() * packing_width;
let lg_n = log2_strict_usize(n);
debug_assert_eq!(root_table.len(), lg_n - 1);
const LAST_LOOP_LAYER: usize = 4;
const NUM_SPECIALISATIONS: usize = 2;
assert!(lg_n >= LAST_LOOP_LAYER + NUM_SPECIALISATIONS);
Self::monty_forward_pass_packed(packed_input, &root_table[lg_n - 2]); Self::monty_forward_iterative_layer_1(packed_input, &root_table[lg_n - 3]);
for lg_m in (LAST_LOOP_LAYER..(lg_n - NUM_SPECIALISATIONS)).rev() {
let m = 1 << lg_m;
let roots = &root_table[lg_m - 1];
debug_assert_eq!(roots.len(), m);
Self::forward_iterative_layer(packed_input, roots, m);
}
Self::forward_iterative_packed_radix_16(packed_input);
}
#[inline(always)]
fn forward_butterfly(x: Self, y: Self, w: Self) -> (Self, Self) {
let t = MP::PRIME + x.value - y.value;
(
x + y,
Self::new_monty(monty_reduce::<MP>(t as u64 * w.value as u64)),
)
}
#[inline]
fn forward_pass(input: &mut [Self], roots: &[Self]) {
let half_n = input.len() / 2;
assert_eq!(roots.len(), half_n);
let (xs, ys) = unsafe { input.split_at_mut_unchecked(half_n) };
let s = xs[0] + ys[0];
let t = xs[0] - ys[0];
xs[0] = s;
ys[0] = t;
izip!(&mut xs[1..], &mut ys[1..], &roots[1..]).for_each(|(x, y, &root)| {
(*x, *y) = Self::forward_butterfly(*x, *y, root);
});
}
#[inline(always)]
fn forward_2(a: &mut [Self]) {
assert_eq!(a.len(), 2);
let s = a[0] + a[1];
let t = a[0] - a[1];
a[0] = s;
a[1] = t;
}
#[inline(always)]
fn forward_4(a: &mut [Self]) {
assert_eq!(a.len(), 4);
let t1 = MP::PRIME + a[1].value - a[3].value;
let t3 = Self::new_monty(monty_reduce::<MP>(
t1 as u64 * MP::ROOTS_8.as_ref()[2].value as u64,
));
let t5 = a[1] + a[3];
let t4 = a[0] + a[2];
let t2 = a[0] - a[2];
a[0] = t4 + t5;
a[1] = t4 - t5;
a[2] = t2 + t3;
a[3] = t2 - t3;
}
#[inline(always)]
fn forward_8(a: &mut [Self]) {
assert_eq!(a.len(), 8);
Self::forward_pass(a, MP::ROOTS_8.as_ref());
let (a0, a1) = unsafe { a.split_at_mut_unchecked(a.len() / 2) };
Self::forward_4(a0);
Self::forward_4(a1);
}
#[inline(always)]
fn forward_16(a: &mut [Self]) {
assert_eq!(a.len(), 16);
Self::forward_pass(a, MP::ROOTS_16.as_ref());
let (a0, a1) = unsafe { a.split_at_mut_unchecked(a.len() / 2) };
Self::forward_8(a0);
Self::forward_8(a1);
}
#[inline(always)]
fn forward_32(a: &mut [Self], root_table: &[Vec<Self>]) {
assert_eq!(a.len(), 32);
Self::forward_pass(a, &root_table[root_table.len() - 1]);
let (a0, a1) = unsafe { a.split_at_mut_unchecked(a.len() / 2) };
Self::forward_16(a0);
Self::forward_16(a1);
}
#[inline]
fn forward_fft_recur(input: &mut [<Self as Field>::Packing], root_table: &[Vec<Self>]) {
const ITERATIVE_FFT_THRESHOLD: usize = 1024;
let n = input.len() * <Self as Field>::Packing::WIDTH;
if n <= ITERATIVE_FFT_THRESHOLD {
Self::forward_iterative(input, root_table);
} else {
assert_eq!(n, 1 << (root_table.len() + 1));
Self::monty_forward_pass_packed(input, &root_table[root_table.len() - 1]);
let (a0, a1) = unsafe { input.split_at_mut_unchecked(input.len() / 2) };
Self::forward_fft_recur(a0, &root_table[..root_table.len() - 1]);
Self::forward_fft_recur(a1, &root_table[..root_table.len() - 1]);
}
}
#[inline]
pub fn forward_fft(input: &mut [Self], root_table: &[Vec<Self>]) {
let n = input.len();
if n == 1 {
return;
}
assert_eq!(n, 1 << (root_table.len() + 1));
match n {
32 => Self::forward_32(input, root_table),
16 => Self::forward_16(input),
8 => Self::forward_8(input),
4 => Self::forward_4(input),
2 => Self::forward_2(input),
_ => {
let packed_input = <Self as Field>::Packing::pack_slice_mut(input);
Self::forward_fft_recur(packed_input, root_table);
}
}
}
}