1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
use std::ops::Deref;
use crate::ring::*;
use crate::homomorphism::*;
use crate::vector::*;
use crate::mempool::*;
use crate::default_memory_provider;
pub mod cooley_tuckey;
pub mod bluestein;
pub mod factor_fft;
pub mod complex_fft;
///
/// Trait for objects that can perform a fast fourier transform over some ring.
///
/// Usually fast implementations of FFTs have to store a lot of precomputed data
/// (e.g. powers of roots of unity), hence they should be represented as objects
/// implementing this trait.
///
/// The trait is very generic, and its functions can be called on any
/// [`VectorView`] of elements of any ring `R` with `R: CanHomFrom<Base<Self::Ring>>`.
/// Of course, the roots of unity of the stored ring must map to corresponding roots
/// of unity in `R` via the canonical homomorphism.
///
/// # Note on equality
///
/// If you choose to implement [`PartialEq`] for an FFTTable, and `F == G`, then
/// `F` and `G` should satisfy the following properties:
/// - `F.ring() == G.ring()`, i.e. elements can be transferred between rings
/// without applying homomorphisms
/// - `F.len() == G.len()`
/// - `F.root_of_unity() == G.root_of_unity()`
/// - `F.unordered_fft_permutation(i) == G.unordered_fft_permutation(i)` for all `i`
/// In other words, `F` and `G` must have exactly the same output for `unordered_fft`
/// (and thus `fft`, `inv_fft`, ...) on same inputs.
///
pub trait FFTTable {
type Ring: ?Sized + RingStore;
///
/// This FFTTable can compute the FFT of arrays of this length.
///
fn len(&self) -> usize;
///
/// The underlying ring whose roots of unity are used by the FFT.
///
fn ring(&self) -> &Self::Ring;
///
/// The root of unity used for the FFT. While all primitive `n`-th roots
/// of unity can be used equally for computing a Fourier transform, the
/// concrete one used determines the order of the output values.
///
/// See also [`FFTTable::unordered_fft_permutation`].
///
fn root_of_unity(&self) -> &El<Self::Ring>;
///
/// On input `i`, returns `j` such that `unordered_fft(values)[i]` contains the evaluation
/// at `zeta^j` of values. Here `zeta` is the value returned by [`FFTTable::root_of_unity()`]
///
fn unordered_fft_permutation(&self, i: usize) -> usize;
///
/// The inverse of [`FFTTable::unordered_fft_permutation()`], i.e. for all i, have
/// `self.unordered_fft_permutation_inv(self.unordered_fft_permutation(i)) == i`.
///
fn unordered_fft_permutation_inv(&self, i: usize) -> usize;
///
/// Computes inplace the Fourier transform of the given `values` over the given `ring`.
/// The output is in standard order, i.e. the `i`-th output element is the evaluation
/// of the input at `self.root_of_unity()^-i` (note the `-`, which is standard
/// convention for Fourier transforms).
///
/// If necessary, temporary memory is allocated using the given memory provider.
/// In some cases, it can be faster to use [`FFTTable::unordered_fft`], if the ordering
/// of the result is not relevant.
///
/// # Panics
///
/// This function panics if `values.len() != self.len()`.
///
fn fft<V, S, M, H>(&self, mut values: V, memory_provider: &M, hom: &H)
where S: ?Sized + RingBase,
H: Homomorphism<<Self::Ring as RingStore>::Type, S>,
V: SwappableVectorViewMut<S::Element>,
M: MemoryProvider<S::Element>
{
self.unordered_fft(&mut values, memory_provider, hom);
permute::permute_inv(&mut values, |i| self.unordered_fft_permutation(i), &default_memory_provider!());
}
///
/// Computes inplace the inverse Fourier transform of the given `values` over the given `ring`.
/// The output is in standard order, i.e. the `i`-th output element is the evaluation
/// of the input at `self.root_of_unity()^i`, divided by `self.len()`.
///
/// If necessary, temporary memory is allocated using the given memory provider.
/// In some cases, it can be faster to use [`FFTTable::unordered_inv_fft`], if the ordering
/// of the result is not relevant.
///
/// # Panics
///
/// This function panics if `values.len() != self.len()`.
///
fn inv_fft<V, S, M, H>(&self, mut values: V, memory_provider: &M, hom: &H)
where S: ?Sized + RingBase,
H: Homomorphism<<Self::Ring as RingStore>::Type, S>,
V: SwappableVectorViewMut<S::Element>,
M: MemoryProvider<S::Element>
{
permute::permute(&mut values, |i| self.unordered_fft_permutation(i), &default_memory_provider!());
self.unordered_inv_fft(&mut values, memory_provider, hom);
}
///
/// Computes the Fourier transform of the given values, but the output values are arbitrarily permuted
/// (in a way compatible with [`FFTTable::unordered_inv_fft()`]).
///
/// This supports any given ring, as long as the precomputed values stored in the table are
/// also contained in the new ring. The result is wrong however if the canonical homomorphism
/// `R -> S` does not map the N-th root of unity to a primitive N-th root of unity.
///
/// Note that the FFT of a sequence `a_0, ..., a_(N - 1)` is defined as `Fa_k = sum_i a_i z^(-ik)`
/// where `z` is an N-th root of unity.
///
/// The given `memory_provider` is used in the case that temporary memory is required, as e.g.
/// for [`crate::algorithms::fft::bluestein::FFTTableBluestein`] .
///
fn unordered_fft<V, S, M, H>(&self, values: V, memory_provider: &M, hom: &H)
where S: ?Sized + RingBase,
H: Homomorphism<<Self::Ring as RingStore>::Type, S>,
V: VectorViewMut<S::Element>,
M: MemoryProvider<S::Element>;
///
/// Inverse to [`Self::unordered_fft()`], with basically the same contract.
///
fn unordered_inv_fft<V, S, M, H>(&self, values: V, memory_provider: &M, hom: &H)
where S: ?Sized + RingBase,
H: Homomorphism<<Self::Ring as RingStore>::Type, S>,
V: VectorViewMut<S::Element>,
M: MemoryProvider<S::Element>;
}
impl<T> FFTTable for T
where T: Deref, T::Target: FFTTable
{
type Ring = <T::Target as FFTTable>::Ring;
fn len(&self) -> usize {
self.deref().len()
}
fn ring(&self) -> &Self::Ring {
self.deref().ring()
}
fn root_of_unity(&self) -> &El<Self::Ring> {
self.deref().root_of_unity()
}
fn unordered_fft_permutation(&self, i: usize) -> usize {
self.deref().unordered_fft_permutation(i)
}
fn unordered_fft_permutation_inv(&self, i: usize) -> usize {
self.deref().unordered_fft_permutation_inv(i)
}
fn fft<V, S, M, H>(&self, values: V, memory_provider: &M, hom: &H)
where S: ?Sized + RingBase,
H: Homomorphism<<Self::Ring as RingStore>::Type, S>,
V: SwappableVectorViewMut<S::Element>,
M: MemoryProvider<S::Element>
{
self.deref().fft(values, memory_provider, hom)
}
fn inv_fft<V, S, M, H>(&self, values: V, memory_provider: &M, hom: &H)
where S: ?Sized + RingBase,
H: Homomorphism<<Self::Ring as RingStore>::Type, S>,
V: SwappableVectorViewMut<S::Element>,
M: MemoryProvider<S::Element>
{
self.deref().inv_fft(values, memory_provider, hom)
}
fn unordered_fft<V, S, M, H>(&self, values: V, memory_provider: &M, hom: &H)
where S: ?Sized + RingBase,
H: Homomorphism<<Self::Ring as RingStore>::Type, S>,
V: VectorViewMut<S::Element>,
M: MemoryProvider<S::Element>
{
self.deref().unordered_fft(values, memory_provider, hom)
}
fn unordered_inv_fft<V, S, M, H>(&self, values: V, memory_provider: &M, hom: &H)
where S: ?Sized + RingBase,
H: Homomorphism<<Self::Ring as RingStore>::Type, S>,
V: VectorViewMut<S::Element>,
M: MemoryProvider<S::Element>
{
self.deref().unordered_inv_fft(values, memory_provider, hom)
}
}