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)
    }
}