Non-recursive implementation of the Cooley-Tukey radix-2 decimation in time. Performs 1D FFT transform for the lengths, which are powers of 2. Runs in O(length * log(length)) time. Uses the same parameters as the naive implementation above, except that the preallocated buffer must be at least twice as big as the length of the transform, because the buffer is used to hold both input and output valu
| 872 | // hold both input and output values for each stage of the transform. |
| 873 | // |
| 874 | void Fft1D(int64 length, int64 start, int64 stride, bool inverse, |
| 875 | bool contract_output, bool expand_input, absl::Span<complex128> data, |
| 876 | absl::Span<complex128> buffer) { |
| 877 | CHECK(IsPowerOfTwo(static_cast<uint64>(length))); |
| 878 | const bool input_is_zero = |
| 879 | GatherToBuffer(data, length, start, stride, expand_input, buffer); |
| 880 | |
| 881 | if (!input_is_zero) { |
| 882 | auto generate_twiddles = [](int64 length, bool inverse) { |
| 883 | std::vector<complex128> twiddles; |
| 884 | // Need only half the twiddles. |
| 885 | for (int64 k = 0; k < length / 2; k++) { |
| 886 | twiddles.push_back(Twiddle(k, length, inverse)); |
| 887 | } |
| 888 | return twiddles; |
| 889 | }; |
| 890 | |
| 891 | // Indices into the parts of the buffer used for input and output values. |
| 892 | int64 in_base = length; |
| 893 | int64 out_base = 0; |
| 894 | |
| 895 | // At each stage, we "split" the input data into num_blocks, with block_size |
| 896 | // values in each block. |
| 897 | for (int64 num_blocks = 1; num_blocks < length; num_blocks *= 2) { |
| 898 | // Swap input and output parts of the buffer. |
| 899 | std::swap(in_base, out_base); |
| 900 | auto twiddles = generate_twiddles(num_blocks * 2, inverse); |
| 901 | const int64 block_size = length / num_blocks; |
| 902 | const int64 next_iteration_block_size = block_size / 2; |
| 903 | for (int64 block = 0; block < num_blocks; block++) { |
| 904 | const int64 in_offset = in_base + block * block_size; |
| 905 | const int64 out_offset = out_base + block * next_iteration_block_size; |
| 906 | // For each (even, odd) pair of values in the block, calculate two |
| 907 | // output values as even + twiddle * odd and even - twiddle * odd. |
| 908 | for (int64 pair = 0; pair < block_size / 2; pair++) { |
| 909 | const complex128 even = buffer[in_offset + pair]; |
| 910 | const complex128 odd = buffer[in_offset + block_size / 2 + pair]; |
| 911 | const complex128 twiddled_odd = twiddles[block] * odd; |
| 912 | buffer[out_offset + pair] = even + twiddled_odd; |
| 913 | buffer[out_offset + length / 2 + pair] = even - twiddled_odd; |
| 914 | } |
| 915 | } |
| 916 | } |
| 917 | // Copy computed result back to data. |
| 918 | const int64 ub = contract_output ? length / 2 + 1 : length; |
| 919 | for (int64 k = 0; k < ub; k++) { |
| 920 | complex128 value = buffer[out_base + k]; |
| 921 | data[start + k * stride] = |
| 922 | inverse ? value / complex128(length, 0.0) : value; |
| 923 | } |
| 924 | } |
| 925 | } |
| 926 | |
| 927 | // Determine, which implementation of 1D transform to use and call it. |
| 928 | void Dft1D(int64 length, int64 start, int64 stride, bool inverse, |
no test coverage detected