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