1 use std::collections::HashMap;
2 use std::sync::Arc;
3 use num_integer::gcd;
4 
5 use common::FFTnum;
6 
7 use FFT;
8 use algorithm::*;
9 use algorithm::butterflies::*;
10 
11 use math_utils;
12 
13 
14 const MIN_RADIX4_BITS: u32 = 5; // smallest size to consider radix 4 an option is 2^5 = 32
15 const MAX_RADIX4_BITS: u32 = 16; // largest size to consider radix 4 an option is 2^16 = 65536
16 const BUTTERFLIES: [usize; 9] = [2, 3, 4, 5, 6, 7, 8, 16, 32];
17 const COMPOSITE_BUTTERFLIES: [usize; 5] = [4, 6, 8, 16, 32];
18 
19 /// The FFT planner is used to make new FFT algorithm instances.
20 ///
21 /// RustFFT has several FFT algorithms available; For a given FFT size, the FFTplanner decides which of the
22 /// available FFT algorithms to use and then initializes them.
23 ///
24 /// ~~~
25 /// // Perform a forward FFT of size 1234
26 /// use std::sync::Arc;
27 /// use rustfft::FFTplanner;
28 /// use rustfft::num_complex::Complex;
29 /// use rustfft::num_traits::Zero;
30 ///
31 /// let mut input:  Vec<Complex<f32>> = vec![Zero::zero(); 1234];
32 /// let mut output: Vec<Complex<f32>> = vec![Zero::zero(); 1234];
33 ///
34 /// let mut planner = FFTplanner::new(false);
35 /// let fft = planner.plan_fft(1234);
36 /// fft.process(&mut input, &mut output);
37 ///
38 /// // The fft instance returned by the planner is stored behind an `Arc`, so it's cheap to clone
39 /// let fft_clone = Arc::clone(&fft);
40 /// ~~~
41 ///
42 /// If you plan on creating multiple FFT instances, it is recommnded to reuse the same planner for all of them. This
43 /// is because the planner re-uses internal data across FFT instances wherever possible, saving memory and reducing
44 /// setup time. (FFT instances created with one planner will never re-use data and buffers with FFT instances created
45 /// by a different planner)
46 ///
47 /// Each FFT instance owns `Arc`s to its internal data, rather than borrowing it from the planner, so it's perfectly
48 /// safe to drop the planner after creating FFT instances.
49 pub struct FFTplanner<T> {
50     inverse: bool,
51     algorithm_cache: HashMap<usize, Arc<FFT<T>>>,
52     butterfly_cache: HashMap<usize, Arc<FFTButterfly<T>>>,
53 }
54 
55 impl<T: FFTnum> FFTplanner<T> {
56     /// Creates a new FFT planner.
57     ///
58     /// If `inverse` is false, this planner will plan forward FFTs. If `inverse` is true, it will plan inverse FFTs.
new(inverse: bool) -> Self59     pub fn new(inverse: bool) -> Self {
60         FFTplanner {
61             inverse: inverse,
62             algorithm_cache: HashMap::new(),
63             butterfly_cache: HashMap::new(),
64         }
65     }
66 
67     /// Returns a FFT instance which processes signals of size `len`
68     /// If this is called multiple times, it will attempt to re-use internal data between instances
plan_fft(&mut self, len: usize) -> Arc<FFT<T>>69     pub fn plan_fft(&mut self, len: usize) -> Arc<FFT<T>> {
70         if len < 2 {
71             Arc::new(DFT::new(len, self.inverse)) as Arc<FFT<T>>
72         } else {
73             let factors = math_utils::prime_factors(len);
74             self.plan_fft_with_factors(len, &factors)
75         }
76     }
77 
plan_butterfly(&mut self, len: usize) -> Arc<FFTButterfly<T>>78     fn plan_butterfly(&mut self, len: usize) -> Arc<FFTButterfly<T>> {
79         let inverse = self.inverse;
80         let instance = self.butterfly_cache.entry(len).or_insert_with(||
81             match len {
82                 2 => Arc::new(Butterfly2::new(inverse)),
83                 3 => Arc::new(Butterfly3::new(inverse)),
84                 4 => Arc::new(Butterfly4::new(inverse)),
85                 5 => Arc::new(Butterfly5::new(inverse)),
86                 6 => Arc::new(Butterfly6::new(inverse)),
87                 7 => Arc::new(Butterfly7::new(inverse)),
88                 8 => Arc::new(Butterfly8::new(inverse)),
89                 16 => Arc::new(Butterfly16::new(inverse)),
90                 32 => Arc::new(Butterfly32::new(inverse)),
91                 _ => panic!("Invalid butterfly size: {}", len),
92             }
93         );
94         Arc::clone(instance)
95     }
96 
plan_fft_with_factors(&mut self, len: usize, factors: &[usize]) -> Arc<FFT<T>>97     fn plan_fft_with_factors(&mut self, len: usize, factors: &[usize]) -> Arc<FFT<T>> {
98         if self.algorithm_cache.contains_key(&len) {
99             Arc::clone(self.algorithm_cache.get(&len).unwrap())
100         } else {
101             let result = if factors.len() == 1 || COMPOSITE_BUTTERFLIES.contains(&len) {
102                 self.plan_fft_single_factor(len)
103 
104             } else if len.trailing_zeros() <= MAX_RADIX4_BITS && len.trailing_zeros() >= MIN_RADIX4_BITS {
105                 //the number of trailing zeroes in len is the number of `2` factors
106                 //ie if len = 2048 * n, len.trailing_zeros() will equal 11 because 2^11 == 2048
107 
108                 if len.is_power_of_two() {
109                     Arc::new(Radix4::new(len, self.inverse))
110                 } else {
111                     let left_len = 1 << len.trailing_zeros();
112                     let right_len = len / left_len;
113 
114                     let (left_factors, right_factors) = factors.split_at(len.trailing_zeros() as usize);
115 
116                     self.plan_mixed_radix(left_len, left_factors, right_len, right_factors)
117                 }
118 
119             } else {
120                 let sqrt = (len as f32).sqrt() as usize;
121                 if sqrt * sqrt == len {
122                     // since len is a perfect square, each of its prime factors is duplicated.
123                     // since we know they're sorted, we can loop through them in chunks of 2 and keep one out of each chunk
124                     // if the stride iterator ever becomes stabilized, it'll be cleaner to use that instead of chunks
125                     let mut sqrt_factors = Vec::with_capacity(factors.len() / 2);
126                     for chunk in factors.chunks(2) {
127                         sqrt_factors.push(chunk[0]);
128                     }
129 
130                     self.plan_mixed_radix(sqrt, &sqrt_factors, sqrt, &sqrt_factors)
131                 } else {
132                     //len isn't a perfect square. greedily take factors from the list until both sides are as close as possible to sqrt(len)
133                     //TODO: We can probably make this more optimal by using a more sophisticated non-greedy algorithm
134                     let mut product = 1;
135                     let mut second_half_index = 1;
136                     for (i, factor) in factors.iter().enumerate() {
137                         if product * *factor > sqrt {
138                             second_half_index = i;
139                             break;
140                         } else {
141                             product = product * *factor;
142                         }
143                     }
144 
145                     //we now know that product is the largest it can be without being greater than len / product
146                     //there's one more thing we can try to make them closer together -- if product * factors[index] < len / product,
147                     if product * factors[second_half_index] < len / product {
148                         product = product * factors[second_half_index];
149                         second_half_index = second_half_index + 1;
150                     }
151 
152                     //we now have our two FFT sizes: product and product / len
153                     let (left_factors, right_factors) = factors.split_at(second_half_index);
154                     self.plan_mixed_radix(product, left_factors, len / product, right_factors)
155                 }
156             };
157             self.algorithm_cache.insert(len, Arc::clone(&result));
158             result
159         }
160     }
161 
plan_mixed_radix(&mut self, left_len: usize, left_factors: &[usize], right_len: usize, right_factors: &[usize]) -> Arc<FFT<T>>162     fn plan_mixed_radix(&mut self,
163                         left_len: usize,
164                         left_factors: &[usize],
165                         right_len: usize,
166                         right_factors: &[usize])
167                         -> Arc<FFT<T>> {
168 
169         let left_is_butterfly = BUTTERFLIES.contains(&left_len);
170         let right_is_butterfly = BUTTERFLIES.contains(&right_len);
171 
172         //if both left_len and right_len are butterflies, use a mixed radix implementation specialized for butterfly sub-FFTs
173         if left_is_butterfly && right_is_butterfly {
174             let left_fft = self.plan_butterfly(left_len);
175             let right_fft = self.plan_butterfly(right_len);
176 
177             // for butterflies, if gcd is 1, we always want to use good-thomas
178             if gcd(left_len, right_len) == 1 {
179                 Arc::new(GoodThomasAlgorithmDoubleButterfly::new(left_fft, right_fft)) as Arc<FFT<T>>
180             } else {
181                 Arc::new(MixedRadixDoubleButterfly::new(left_fft, right_fft)) as Arc<FFT<T>>
182             }
183         } else {
184             //neither size is a butterfly, so go with the normal algorithm
185             let left_fft = self.plan_fft_with_factors(left_len, left_factors);
186             let right_fft = self.plan_fft_with_factors(right_len, right_factors);
187 
188             Arc::new(MixedRadix::new(left_fft, right_fft)) as Arc<FFT<T>>
189         }
190     }
191 
192 
plan_fft_single_factor(&mut self, len: usize) -> Arc<FFT<T>>193     fn plan_fft_single_factor(&mut self, len: usize) -> Arc<FFT<T>> {
194         match len {
195             0|1 => Arc::new(DFT::new(len, self.inverse)) as Arc<FFT<T>>,
196             2 => Arc::new(butterflies::Butterfly2::new(self.inverse)) as Arc<FFT<T>>,
197             3 => Arc::new(butterflies::Butterfly3::new(self.inverse)) as Arc<FFT<T>>,
198             4 => Arc::new(butterflies::Butterfly4::new(self.inverse)) as Arc<FFT<T>>,
199             5 => Arc::new(butterflies::Butterfly5::new(self.inverse)) as Arc<FFT<T>>,
200             6 => Arc::new(butterflies::Butterfly6::new(self.inverse)) as Arc<FFT<T>>,
201             7 => Arc::new(butterflies::Butterfly7::new(self.inverse)) as Arc<FFT<T>>,
202             8 => Arc::new(butterflies::Butterfly8::new(self.inverse)) as Arc<FFT<T>>,
203             16 => Arc::new(butterflies::Butterfly16::new(self.inverse)) as Arc<FFT<T>>,
204             32 => Arc::new(butterflies::Butterfly32::new(self.inverse)) as Arc<FFT<T>>,
205             _ => self.plan_prime(len),
206         }
207     }
208 
plan_prime(&mut self, len: usize) -> Arc<FFT<T>>209     fn plan_prime(&mut self, len: usize) -> Arc<FFT<T>> {
210         let inner_fft_len = len - 1;
211         let factors = math_utils::prime_factors(inner_fft_len);
212 
213         let inner_fft = self.plan_fft_with_factors(inner_fft_len, &factors);
214 
215         Arc::new(RadersAlgorithm::new(len, inner_fft)) as Arc<FFT<T>>
216     }
217 }
218