/*************************************************************************** * GFft.hpp - Fast Fourier transformation class * * ----------------------------------------------------------------------- * * copyright (C) 2016 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GFft.cpp * @brief Fast Fourier transformation class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include "GTools.hpp" #include "GMath.hpp" #include "GException.hpp" #include "GFft.hpp" #include "GNdarray.hpp" /* __ Method name definitions ____________________________________________ */ #define G_OP_ADD "GFft::operator+=(GFft&)" #define G_OP_SUB "GFft::operator-=(GFft&)" #define G_OP_MUL "GFft::operator*=(GFft&)" #define G_OP_DIV "GFft::operator/=(GFft&)" #define G_FORWARD "GFft::forward(GNdarray&)" #define G_BACKWARD "GFft::backward()" /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GFft::GFft(void) { // Initialise class members init_members(); // Return return; } /***********************************************************************//** * @brief N-dimensional array constructor * * @param[in] array N-dimensional array. * * Constructs a Fast Fourier Transformation from a n-dimensional array. ***************************************************************************/ GFft::GFft(const GNdarray& array) { // Initialise class members init_members(); // Perform foward transformation forward(array); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] fft Fast Fourier Transform. ***************************************************************************/ GFft::GFft(const GFft& fft) { // Initialise class members init_members(); // Copy members copy_members(fft); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GFft::~GFft(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] fft Fast Fourier Transform. * @return Fast Fourier Transform. ***************************************************************************/ GFft& GFft::operator=(const GFft& fft) { // Execute only if object is not identical if (this != &fft) { // Free members free_members(); // Initialise private members init_members(); // Copy members copy_members(fft); } // endif: object was not identical // Return this object return *this; } /***********************************************************************//** * @brief Unary addition operator * * @param[in] fft Fast Fourier Transform. * @return Fast Fourier Transform. * * Adds a Fast Fourier Transform to another Fast Fourier Transform. The * method requires that both FFTs have the same dimension. ***************************************************************************/ GFft& GFft::operator+=(const GFft& fft) { // Throw an exception if the FFTs have not the same shape require_same_shape(G_OP_ADD, fft); // Add elements for (int i = 0; i < m_size; ++i) { *(m_data+i) += *(fft.m_data+i); } // Return Fast Fourier Transform return *this; } /***********************************************************************//** * @brief Unary subtraction operator * * @param[in] fft Fast Fourier transform. * @return Fast Fourier transform. * * Subtracts a Fast Fourier Transform from another Fast Fourier Transform. * The method requires that both FFTs have the same dimension. ***************************************************************************/ GFft& GFft::operator-=(const GFft& fft) { // Throw an exception if the FFTs have not the same shape require_same_shape(G_OP_SUB, fft); // Subtract elements for (int i = 0; i < m_size; ++i) { *(m_data+i) -= *(fft.m_data+i); } // Return Fast Fourier Transform return *this; } /***********************************************************************//** * @brief Unary multiplication operator * * @param[in] fft Fast Fourier transform. * @return Fast Fourier transform. * * Multiplies a Fast Fourier Transform to another Fast Fourier Transform. * The method requires that both FFTs have the same dimension. ***************************************************************************/ GFft& GFft::operator*=(const GFft& fft) { // Throw an exception if the FFTs have not the same shape require_same_shape(G_OP_MUL, fft); // Multiply elements for (int i = 0; i < m_size; ++i) { *(m_data+i) *= *(fft.m_data+i); } // Return Fast Fourier Transform return *this; } /***********************************************************************//** * @brief Unary division operator * * @param[in] fft Fast Fourier transform. * @return Fast Fourier transform. * * Divides a Fast Fourier Transform by another Fast Fourier Transform. * The method requires that both FFTs have the same dimension. ***************************************************************************/ GFft& GFft::operator/=(const GFft& fft) { // Throw an exception if the FFTs have not the same shape require_same_shape(G_OP_DIV, fft); // Divide elements for (int i = 0; i < m_size; ++i) { *(m_data+i) /= *(fft.m_data+i); } // Return Fast Fourier Transform return *this; } /***********************************************************************//** * @brief Unary minus operator * * @return Fast Fourier transformation. * * Negates all elements of the Fast Fourier Transformation. ***************************************************************************/ GFft GFft::operator-(void) const { // Copy FFT GFft fft = *this; // Negate all elements for (int i = 0; i < fft.size(); ++i) { fft(i) = -fft(i); } // Return FFT return fft; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear Fast Fourier Transform ***************************************************************************/ void GFft::clear(void) { // Free members free_members(); // Initialise private members init_members(); // Return return; } /***********************************************************************//** * @brief Clone Fast Fourier Transform * * @return Pointer to deep copy of Fast Fourier Transform. ***************************************************************************/ GFft* GFft::clone(void) const { // Clone FFT return new GFft(*this); } /***********************************************************************//** * @brief Forward Fast Fourier Transform * * @param[in] array N-dimensional array. * * GException::feature_not_implemented * Array dimension is not supported. * * Performs discret Fast Fourier forward transformation for 1- and * 2-dimensional arrays. * * For one dimensional arrays of length \f$N\f$ the transformation is given * by * * \f[ * X_k = \sum_{n=0}^{N-1} z_n e^{-i 2 \pi \frac{k n}{N}} * \f] * * and for two dimensional arrays of length \f$ M \times N\f$ the * transformation is given by * * \f[ * X_{k,l} = \sum_{n=0}^{N-1} \left( * \sum_{m=0}^{M-1} z_{m,n} e^{-i 2 \pi \frac{m k}{M}} \right) * e^{-i 2 \pi \frac{n l}{N}} * \f] ***************************************************************************/ void GFft::forward(const GNdarray& array) { // Set data set_data(array); // Continue only if there are elements in the data member if (m_size > 0) { // Case A: array is 1-dimensional if (array.dim() == 1) { transform(m_data, 1, m_shape[0], m_wavetable[0], true); } // Case B: array is 2-dimensional else if (array.dim() == 2) { // Loop over rows for (int row = 0; row < m_shape[1]; ++row) { transform(m_data + row*m_strides[1], m_strides[0], m_shape[0], m_wavetable[0], true); } // Loop over columns for (int col = 0; col < m_shape[0]; ++col) { transform(m_data + col*m_strides[0], m_strides[1], m_shape[1], m_wavetable[1], true); } } // ... otherwise throw an exception else { std::string msg = "Fast Fourier Transformation for "+ gammalib::str(array.dim())+"-dimensional array " "not implemented."; throw GException::feature_not_implemented(G_FORWARD, msg); } } // endif: there were elements in the FFT data member // Return return; } /***********************************************************************//** * @brief Backward Fast Fourier Transform * * @return N-dimensional array. * * GException::feature_not_implemented * Array dimension is not supported. * * Performs the inverse (or backward) discret Fast Fourier forward * transformation for 1- and 2-dimensional arrays. * * For one dimensional arrays of length \f$N\f$ the transformation is given * by * * \f[ * z_k = \frac{1}{N} \sum_{n=0}^{N-1} X_n e^{i 2 \pi \frac{k n}{N}} * \f] * * and for two dimensional arrays of length \f$ M \times N\f$ the * transformation is given by * * \f[ * z_{k,l} = \frac{1}{N} \sum_{n=0}^{N-1} \left( * \frac{1}{M} \sum_{m=0}^{M-1} X_{m,n} * e^{i 2 \pi \frac{m k}{M}} \right) * e^{i 2 \pi \frac{n l}{N}} * \f] ***************************************************************************/ GNdarray GFft::backward(void) const { // Initialise n-dimensional array GNdarray array(shape()); // Continue only if there are elements in the array if (m_size > 0) { // Create copy of complex array std::complex* data = new std::complex[m_size]; for (int i = 0; i < m_size; ++i) { *(data+i) = *(m_data+i); } // Case A: array is 1-dimensional if (array.dim() == 1) { // Perform backward transformation (circumvent const correctness) const_cast(this)->transform(data, 1, m_shape[0], m_wavetable[0], false); // Get normalisation factor const double norm = 1.0 / double(m_shape[0]); // Extract 1-dimensional array and normalise with 1/n for (int i = 0; i < m_size; ++i) { array(i) = (data+i)->real() * norm; } } // Case B: array is 2-dimensional else if (array.dim() == 2) { // Loop over rows for (int row = 0; row < m_shape[1]; ++row) { const_cast(this)->transform(data + row*m_strides[1], m_strides[0], m_shape[0], m_wavetable[0], false); } // Loop over columns for (int col = 0; col < m_shape[0]; ++col) { const_cast(this)->transform(data + col*m_strides[0], m_strides[1], m_shape[1], m_wavetable[1], false); } // Get normalisation factor const double norm = 1.0 / double(m_shape[0]*m_shape[1]); // Extract 2-dimensional array and normalise with 1/(n*m) for (int i = 0; i < m_size; ++i) { array(i) = (data+i)->real() * norm; } } // ... otherwise throw an exception else { // Delete copy of complex array delete [] data; // Throw exception std::string msg = "Fast Fourier Transformation for "+ gammalib::str(array.dim())+"-dimensional array " "not implemented."; throw GException::feature_not_implemented(G_BACKWARD, msg); } // Delete copy of complex array delete [] data; } // endif: there were elements in the array // Return N-dimensional array return array; } /***********************************************************************//** * @brief Print Fast Fourier Transform information * * @param[in] chatter Chattiness. * @return String containing Fast Fourier Transform information. ***************************************************************************/ std::string GFft::print(const GChatter& chatter) const { // Initialise result string std::string result; // Continue only if chatter is not silent if (chatter != SILENT) { // Append header result.append("=== GFft ==="); // Append array information result.append("\n"+gammalib::parformat("Dimension")); result.append(gammalib::str(dim())); result.append("\n"+gammalib::parformat("Shape")); result.append("("); for (int i = 0; i < dim(); ++i) { if (i > 0) { result.append(","); } result.append(gammalib::str(m_shape[i])); } result.append(")"); result.append("\n"+gammalib::parformat("Size")); result.append(gammalib::str(m_size)); // VERBOSE: Put all FFT elements in string if (chatter >= VERBOSE) { result.append("\n"+gammalib::parformat("Elements")); for (int i = 0; i < size(); ++i) { if (i > 0) { result.append(","); } result.append(gammalib::str((*this)(i))); } result.append(")"); } } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GFft::init_members(void) { // Initialise members m_size = 0; m_data = NULL; m_shape.clear(); m_strides.clear(); m_wavetable.clear(); // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] fft Fast Fourier Transform. ***************************************************************************/ void GFft::copy_members(const GFft& fft) { // Copy members m_size = fft.m_size; m_shape = fft.m_shape; m_strides = fft.m_strides; m_wavetable = fft.m_wavetable; // Copy data if (m_size > 0) { m_data = new std::complex[m_size]; for (int i = 0; i < m_size; ++i) { *(m_data+i) = *(fft.m_data+i); } } // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GFft::free_members(void) { // Delete data if (m_data != NULL) { delete [] m_data; } // Reset size and pointer m_size = 0; m_data = NULL; // Return return; } /***********************************************************************//** * @brief Set data from n-dimensional array * * @param[in] array N-dimensional array ***************************************************************************/ void GFft::set_data(const GNdarray& array) { // Clear information free_members(); init_members(); // Continue only if there are elements in the array if (array.size() > 0) { // Set size of array m_size = array.size(); // Allocate data and initialise data to zero m_data = new std::complex[m_size]; // Set real part of data from n-dimensional array for (int i = 0; i < m_size; ++i) { *(m_data+i) = std::complex(array(i), 0.0); } // Save shape and strides m_shape = array.shape(); m_strides = array.strides(); // Set trigonometric coefficients for (int i = 0; i < m_shape.size(); ++i) { GFftWavetable wavetable(m_shape[i]); m_wavetable.push_back(wavetable); } } // endif: there were elements in the array // Return return; } /***********************************************************************//** * @brief Check if FFT has the same shape * * @param[in] fft Fast Fourier Transform. * @return True if the FFT has the same shape. ***************************************************************************/ bool GFft::has_same_shape(const GFft& fft) const { // Check if the array has the same dimension bool identity = m_shape.size() == fft.m_shape.size(); // Check if the shape is identical. Break as soon as one shape value // differs. if (identity) { for (int i = 0; i < m_shape.size(); ++i) { if (m_shape[i] != fft.m_shape[i]) { identity = false; break; } } } // Return result return identity; } /***********************************************************************//** * @brief Throw exception if FFT shapes differ * * @param[in] method Method that throws exception. * @param[in] fft Fast Fourier Transform. * * @exception GException::invalid_argument * FFT shapes differ. ***************************************************************************/ void GFft::require_same_shape(const std::string& method, const GFft& fft) const { // If the shape differs then throw an exception if (!has_same_shape(fft)) { // Compose exception message std::string msg = "Incompatible FFT dimensions ("; for (int i = 0; i < m_shape.size(); ++i) { if (i > 0) { msg += ", "; } msg += gammalib::str(m_shape[i]); } msg += ") <=> ("; for (int i = 0; i < fft.m_shape.size(); ++i) { if (i > 0) { msg += ", "; } msg += gammalib::str(fft.m_shape[i]); } msg += ")."; // Throw exception throw GException::invalid_argument(method, msg); } // endif: array shapes differed // Return return; } /***********************************************************************//** * @brief Perform Fast Fourier Transform * * @param[in] data Pointer to complex array to be transformed. * @param[in] stride Step size when traversing complex array. * @param[in] n Number of elements in complex array. * @param[in] wavetable Trigonometric coefficients. * @param[in] forward Forward transform if true, otherwise backward transform. ***************************************************************************/ void GFft::transform(std::complex* data, const int& stride, const int& n, const GFftWavetable& wavetable, const bool& forward) { // Allocate scratch space and initialize to zero std::complex* scratch = new std::complex[n]; std::complex zero(0.0, 0.0); for (int i = 0; i < n; ++i) { *(scratch+i) = zero; } // Initialise transformation state int state = 0; // Initialise factorisation product int product = 1; // Set sign int sign = (forward) ? -1 : +1; // Loop over all factors for (int i = 0; i < wavetable.factors(); ++i) { // Get factorisation factor and start index int factor = wavetable.factor(i); int index = wavetable.index(i); // Update factorisation product product *= factor; // Compute number of coefficients per factor int q = n / product; // Set state dependent stuff std::complex* in; std::complex* out; int istride; int ostride; if (state == 0) { in = data; istride = stride; out = scratch; ostride = 1; state = 1; } else { in = scratch; istride = 1; out = data; ostride = stride; state = 0; } // Call factor dependent method if (factor == 2) { factor2(in, istride, out, ostride, wavetable, sign, product, n, index); } else if (factor == 3) { factor3(in, istride, out, ostride, wavetable, sign, product, n, index); } else if (factor == 4) { factor4(in, istride, out, ostride, wavetable, sign, product, n, index); } else if (factor == 5) { factor5(in, istride, out, ostride, wavetable, sign, product, n, index); } else if (factor == 6) { factor6(in, istride, out, ostride, wavetable, sign, product, n, index); } else if (factor == 7) { factor7(in, istride, out, ostride, wavetable, sign, product, n, index); } else { factorn(in, istride, out, ostride, wavetable, sign, factor, product, n, index); } } // endfor: looped over all factors // In case the loop was exited in state 1 the results are in the scratch // array and we need to copy the results back from the scratch array to // the data array if (state == 1) { for (int i = 0; i < n; ++i) { *(data+stride*i) = *(scratch+i); } } // Free scratch space delete [] scratch; // Return return; } /***********************************************************************//** * @brief Compute FFT for factor 2 * * @param[in] in Pointer to input array. * @param[in] istride Step size when traversing input array. * @param[in] out Pointer to output array. * @param[in] ostride Step size when traversing output array. * @param[in] wavetable Trigonometric coefficients. * @param[in] sign Forward (-1) or backward (+1). * @param[in] product ... * @param[in] n Logical array length. * @param[in] index Start index of trigonometric coefficients. ***************************************************************************/ void GFft::factor2(const std::complex* in, const int& istride, std::complex* out, const int& ostride, const GFftWavetable& wavetable, const int& sign, const int& product, const int& n, const int& index) { // Compute ... const int factor = 2; const int m = n / factor; const int q = n / product; const int p_1 = product / factor; const int jump = (factor - 1) * p_1; // Loop over ... for (int k = 0, i = 0, j = 0; k < q; ++k, j += jump) { // Extract coefficients from wavetable std::vector > w = get_w(wavetable, index, k, q, 1, sign); // Compute x = W(2) z for (int k1 = 0; k1 < p_1; ++k1, ++i, ++j) { // Get z const std::complex z0 = *(in+istride*i); const std::complex z1 = *(in+istride*(i+m)); // Compute x const std::complex x0 = z0 + z1; const std::complex x1 = z0 - z1; // out = w * x *(out+ostride*j) = x0; *(out+ostride*(j+p_1)) = w[0] * x1; } // endfor: k1 } // endfor: k // Return return; } /***********************************************************************//** * @brief Compute FFT for factor 3 * * @param[in] in Pointer to input array. * @param[in] istride Step size when traversing input array. * @param[in] out Pointer to output array. * @param[in] ostride Step size when traversing output array. * @param[in] wavetable Trigonometric coefficients. * @param[in] sign Forward (-1) or backward (+1). * @param[in] product ... * @param[in] n Logical array length. * @param[in] index Start index of trigonometric coefficients. ***************************************************************************/ void GFft::factor3(const std::complex* in, const int& istride, std::complex* out, const int& ostride, const GFftWavetable& wavetable, const int& sign, const int& product, const int& n, const int& index) { // Compute ... const int factor = 3; const int m = n / factor; const int q = n / product; const int p_1 = product / factor; const int jump = (factor - 1) * p_1; // Precompute some factors const double tau = std::sqrt(3.0) / 2.0; // Loop over ... for (int k = 0, i = 0, j = 0; k < q; ++k, j += jump) { // Extract coefficients from wavetable std::vector > w = get_w(wavetable, index, k, q, 2, sign); // Compute x = W(3) z for (int k1 = 0; k1 < p_1; ++k1, ++i, ++j) { // Get z const std::complex z0 = *(in+istride*i); const std::complex z1 = *(in+istride*(i+m)); const std::complex z2 = *(in+istride*(i+2*m)); // Compute t const std::complex t1 = z1 + z2; const std::complex t2 = z0 - t1/2.0; const std::complex t3 = double((int)sign) * tau * (z1 - z2); // Compute x const std::complex x0 = z0 + t1; const std::complex x1 = t2 + timesi(t3); const std::complex x2 = t2 - timesi(t3); // out = w * x *(out+ostride*j) = x0; *(out+ostride*(j+p_1)) = w[0] * x1; *(out+ostride*(j+2*p_1)) = w[1] * x2; } // endfor: k1 } // endfor: k // Return return; } /***********************************************************************//** * @brief Compute FFT for factor 4 * * @param[in] in Pointer to input array. * @param[in] istride Step size when traversing input array. * @param[in] out Pointer to output array. * @param[in] ostride Step size when traversing output array. * @param[in] wavetable Trigonometric coefficients. * @param[in] sign Forward (-1) or backward (+1). * @param[in] product ... * @param[in] n Logical array length. * @param[in] index Start index of trigonometric coefficients. ***************************************************************************/ void GFft::factor4(const std::complex* in, const int& istride, std::complex* out, const int& ostride, const GFftWavetable& wavetable, const int& sign, const int& product, const int& n, const int& index) { // Compute ... const int factor = 4; const int m = n / factor; const int q = n / product; const int p_1 = product / factor; const int jump = (factor - 1) * p_1; // Loop over ... for (int k = 0, i = 0, j = 0; k < q; ++k, j += jump) { // Extract coefficients from wavetable std::vector > w = get_w(wavetable, index, k, q, 3, sign); // Compute x = W(4) z for (int k1 = 0; k1 < p_1; ++k1, ++i, ++j) { // Get z const std::complex z0 = *(in+istride*i); const std::complex z1 = *(in+istride*(i+m)); const std::complex z2 = *(in+istride*(i+2*m)); const std::complex z3 = *(in+istride*(i+3*m)); // Compute t const std::complex t1 = z0 + z2; const std::complex t2 = z1 + z3; const std::complex t3 = z0 - z2; const std::complex t4 = double((int)sign) * (z1 - z3); // Compute x const std::complex x0 = t1 + t2; const std::complex x1 = t3 + timesi(t4); const std::complex x2 = t1 - t2; const std::complex x3 = t3 - timesi(t4); // out = w * x *(out+ostride*j) = x0; *(out+ostride*(j+p_1)) = w[0] * x1; *(out+ostride*(j+2*p_1)) = w[1] * x2; *(out+ostride*(j+3*p_1)) = w[2] * x3; } // endfor: k1 } // endfor: k // Return return; } /***********************************************************************//** * @brief Compute FFT for factor 5 * * @param[in] in Pointer to input array. * @param[in] istride Step size when traversing input array. * @param[in] out Pointer to output array. * @param[in] ostride Step size when traversing output array. * @param[in] wavetable Trigonometric coefficients. * @param[in] sign Forward (-1) or backward (+1). * @param[in] product ... * @param[in] n Logical array length. * @param[in] index Start index of trigonometric coefficients. ***************************************************************************/ void GFft::factor5(const std::complex* in, const int& istride, std::complex* out, const int& ostride, const GFftWavetable& wavetable, const int& sign, const int& product, const int& n, const int& index) { // Compute ... const int factor = 5; const int m = n / factor; const int q = n / product; const int p_1 = product / factor; const int jump = (factor - 1) * p_1; // Precompute some factors const double sin_2pi_by_5 = std::sin(gammalib::twopi / 5.0); const double sin_2pi_by_10 = std::sin(gammalib::twopi / 10.0); const double tau = std::sqrt(5.0) / 4.0; // Loop over ... for (int k = 0, i = 0, j = 0; k < q; ++k, j += jump) { // Extract coefficients from wavetable std::vector > w = get_w(wavetable, index, k, q, 4, sign); // Compute x = W(5) z for (int k1 = 0; k1 < p_1; ++k1, ++i, ++j) { // Get z const std::complex z0 = *(in+istride*i); const std::complex z1 = *(in+istride*(i+m)); const std::complex z2 = *(in+istride*(i+2*m)); const std::complex z3 = *(in+istride*(i+3*m)); const std::complex z4 = *(in+istride*(i+4*m)); // Compute t const std::complex t1 = z1 + z4; const std::complex t2 = z2 + z3; const std::complex t3 = z1 - z4; const std::complex t4 = z2 - z3; const std::complex t5 = t1 + t2; const std::complex t6 = tau * (t1 - t2); const std::complex t7 = z0 - t5/4.0; const std::complex t8 = t7 + t6; const std::complex t9 = t7 - t6; const std::complex t10 = double((int)sign) * (sin_2pi_by_5*t3 + sin_2pi_by_10*t4); const std::complex t11 = double((int)sign) * (sin_2pi_by_10*t3 - sin_2pi_by_5*t4); // Compute x const std::complex x0 = z0 + t5; const std::complex x1 = t8 + timesi(t10); const std::complex x2 = t9 + timesi(t11); const std::complex x3 = t9 - timesi(t11); const std::complex x4 = t8 - timesi(t10); // Compute out = w * x *(out+ostride*j) = x0; *(out+ostride*(j+p_1)) = w[0] * x1; *(out+ostride*(j+2*p_1)) = w[1] * x2; *(out+ostride*(j+3*p_1)) = w[2] * x3; *(out+ostride*(j+4*p_1)) = w[3] * x4; } // endfor: k1 } // endfor: k // Return return; } /***********************************************************************//** * @brief Compute FFT for factor 6 * * @param[in] in Pointer to input array. * @param[in] istride Step size when traversing input array. * @param[in] out Pointer to output array. * @param[in] ostride Step size when traversing output array. * @param[in] wavetable Trigonometric coefficients. * @param[in] sign Forward (-1) or backward (+1). * @param[in] product ... * @param[in] n Logical array length. * @param[in] index Start index of trigonometric coefficients. ***************************************************************************/ void GFft::factor6(const std::complex* in, const int& istride, std::complex* out, const int& ostride, const GFftWavetable& wavetable, const int& sign, const int& product, const int& n, const int& index) { // Compute ... const int factor = 6; const int m = n / factor; const int q = n / product; const int p_1 = product / factor; const int jump = (factor - 1) * p_1; // Precompute some factors const double tau = std::sqrt(3.0) / 2.0; // Loop over ... for (int k = 0, i = 0, j = 0; k < q; ++k, j += jump) { // Extract coefficients from wavetable std::vector > w = get_w(wavetable, index, k, q, 5, sign); // Compute x = W(6) z. W(6) is a combination of sums and differences of // W(3) acting on the even and odd elements of z for (int k1 = 0; k1 < p_1; ++k1, ++i, ++j) { // Get z const std::complex z0 = *(in+istride*i); const std::complex z1 = *(in+istride*(i+m)); const std::complex z2 = *(in+istride*(i+2*m)); const std::complex z3 = *(in+istride*(i+3*m)); const std::complex z4 = *(in+istride*(i+4*m)); const std::complex z5 = *(in+istride*(i+5*m)); // Compute ta const std::complex ta1 = z2 + z4; const std::complex ta2 = z0 - ta1/2.0; const std::complex ta3 = double((int)sign) * tau * (z2 - z4); // Compute a const std::complex a0 = z0 + ta1; const std::complex a1 = ta2 + timesi(ta3); const std::complex a2 = ta2 - timesi(ta3); // Compute tb const std::complex tb1 = z5 + z1; const std::complex tb2 = z3 - tb1/2.0; const std::complex tb3 = double((int)sign) * tau * (z5 - z1); // Compute b const std::complex b0 = z3 + tb1; const std::complex b1 = tb2 + timesi(tb3); const std::complex b2 = tb2 - timesi(tb3); // Compute x const std::complex x0 = a0 + b0; const std::complex x1 = a1 - b1; const std::complex x2 = a2 + b2; const std::complex x3 = a0 - b0; const std::complex x4 = a1 + b1; const std::complex x5 = a2 - b2; // Compute out = w * x *(out+ostride*j) = x0; *(out+ostride*(j+p_1)) = w[0] * x1; *(out+ostride*(j+2*p_1)) = w[1] * x2; *(out+ostride*(j+3*p_1)) = w[2] * x3; *(out+ostride*(j+4*p_1)) = w[3] * x4; *(out+ostride*(j+5*p_1)) = w[4] * x5; } // endfor: k1 } // endfor: k // Return return; } /***********************************************************************//** * @brief Compute FFT for factor 7 * * @param[in] in Pointer to input array. * @param[in] istride Step size when traversing input array. * @param[in] out Pointer to output array. * @param[in] ostride Step size when traversing output array. * @param[in] wavetable Trigonometric coefficients. * @param[in] sign Forward (-1) or backward (+1). * @param[in] product ... * @param[in] n Logical array length. * @param[in] index Start index of trigonometric coefficients. ***************************************************************************/ void GFft::factor7(const std::complex* in, const int& istride, std::complex* out, const int& ostride, const GFftWavetable& wavetable, const int& sign, const int& product, const int& n, const int& index) { // Compute ... const int factor = 7; const int m = n / factor; const int q = n / product; const int p_1 = product / factor; const int jump = (factor - 1) * p_1; // Precompute some factors static const double twopi7 = gammalib::twopi / 7.0; static const double c1 = std::cos(1.0 * twopi7); static const double c2 = std::cos(2.0 * twopi7); static const double c3 = std::cos(3.0 * twopi7); static const double s1 = std::sin(1.0 * twopi7); static const double s2 = std::sin(2.0 * twopi7); static const double s3 = std::sin(3.0 * twopi7); static const double tau1 = (c1 + c2 + c3) / 3.0 - 1.0; static const double tau2 = (2.0 * c1 - c2 - c3) / 3.0; static const double tau3 = (c1 - 2.0*c2 + c3) / 3.0; static const double tau4 = (c1 + c2 - 2.0 * c3) / 3.0; static const double tau5 = (s1 + s2 - s3) / 3.0; static const double tau6 = (2.0 * s1 - s2 + s3) / 3.0; static const double tau7 = (s1 - 2.0 * s2 - s3) / 3.0; static const double tau8 = (s1 + s2 + 2.0 * s3) / 3.0; // Loop over ... for (int k = 0, i = 0, j = 0; k < q; ++k, j += jump) { // Extract coefficients from wavetable std::vector > w = get_w(wavetable, index, k, q, 6, sign); // Compute x = W(7) z for (int k1 = 0; k1 < p_1; ++k1, ++i, ++j) { // Get z const std::complex z0 = *(in+istride*i); const std::complex z1 = *(in+istride*(i+m)); const std::complex z2 = *(in+istride*(i+2*m)); const std::complex z3 = *(in+istride*(i+3*m)); const std::complex z4 = *(in+istride*(i+4*m)); const std::complex z5 = *(in+istride*(i+5*m)); const std::complex z6 = *(in+istride*(i+6*m)); // Compute t const std::complex t0 = z1 + z6; const std::complex t1 = z1 - z6; const std::complex t2 = z2 + z5; const std::complex t3 = z2 - z5; const std::complex t4 = z4 + z3; const std::complex t5 = z4 - z3; const std::complex t6 = t2 + t0; const std::complex t7 = t5 + t3; // Compute b const std::complex b0 = z0 + t6 + t4; const std::complex b1 = tau1 * (t6 + t4); const std::complex b2 = tau2 * (t0 - t4); const std::complex b3 = tau3 * (t4 - t2); const std::complex b4 = tau4 * (t2 - t0); const std::complex b5 = double(-(int)sign) * tau5 * (t7 + t1); const std::complex b6 = double(-(int)sign) * tau6 * (t1 - t5); const std::complex b7 = double(-(int)sign) * tau7 * (t5 - t3); const std::complex b8 = double(-(int)sign) * tau8 * (t3 - t1); // Compute T const std::complex T0 = b0 + b1; const std::complex T1 = b2 + b3; const std::complex T2 = b4 - b3; const std::complex T3 = -b2 - b4; const std::complex T4 = b6 + b7; const std::complex T5 = b8 - b7; const std::complex T6 = -b8 - b6; const std::complex T7 = T0 + T1; const std::complex T8 = T0 + T2; const std::complex T9 = T0 + T3; const std::complex T10 = T4 + b5; const std::complex T11 = T5 + b5; const std::complex T12 = T6 + b5; // Compute x const std::complex x0 = b0; const std::complex x1 = T7 - timesi(T10); const std::complex x2 = T9 - timesi(T12); const std::complex x3 = T8 + timesi(T11); const std::complex x4 = T8 - timesi(T11); const std::complex x5 = T9 + timesi(T12); const std::complex x6 = T7 + timesi(T10); // Compute out = w * x *(out+ostride*j) = x0; *(out+ostride*(j+p_1)) = w[0] * x1; *(out+ostride*(j+2*p_1)) = w[1] * x2; *(out+ostride*(j+3*p_1)) = w[2] * x3; *(out+ostride*(j+4*p_1)) = w[3] * x4; *(out+ostride*(j+5*p_1)) = w[4] * x5; *(out+ostride*(j+6*p_1)) = w[5] * x6; } // endfor: k1 } // endfor: k // Return return; } /***********************************************************************//** * @brief Compute FFT for arbitrary factor * * @param[in] in Pointer to input array. * @param[in] istride Step size when traversing input array. * @param[in] out Pointer to output array. * @param[in] ostride Step size when traversing output array. * @param[in] wavetable Trigonometric coefficients. * @param[in] sign Forward (-1) or backward (+1). * @param[in] factor Factor. * @param[in] product ... * @param[in] n Logical array length. * @param[in] index Start index of trigonometric coefficients. ***************************************************************************/ void GFft::factorn(std::complex* in, const int& istride, std::complex* out, const int& ostride, const GFftWavetable& wavetable, const int& sign, const int& factor, const int& product, const int& n, const int& index) { // Compute ... const int m = n / factor; const int q = n / product; const int p_1 = product / factor; const int jump = (factor - 1) * p_1; // ... for (int i = 0; i < m; ++i) { *(out+ostride*i) = *(in+istride*i); } // ... for (int e = 1; e < (factor - 1) / 2 + 1; ++e) { for (int i = 0; i < m; ++i) { const int idx = i + e * m; const int idxc = i + (factor - e) * m; *(out+ostride*idx) = *(in+istride*idx) + *(in+istride*idxc); *(out+ostride*idxc) = *(in+istride*idx) - *(in+istride*idxc); } } // e = 0 for (int i = 0; i < m; ++i) { *(in+istride*i) = *(out+ostride*i); } for (int e = 1; e < (factor - 1) / 2 + 1; ++e) { for (int i = 0; i < m; ++i) { const int idx = i + e * m; *(in+istride*i) += *(out+ostride*idx); } } // ... for (int e = 1; e < (factor-1)/2 + 1; ++e) { // int idx = e * q; const int idx_step = e * q; // double w_real, w_imag; // const int em = e * m; const int ecm = (factor - e) * m; // ... for (int i = 0; i < m; ++i) { *(in+istride*(i+em)) = *(out+ostride*i); *(in+istride*(i+ecm)) = *(out+ostride*i); } // ... for (int e1 = 1; e1 < (factor - 1) / 2 + 1; ++e1) { // if (idx == 0) { w_real = 1.0; w_imag = 0.0; } else { // Compute indices int twiddle = index + idx - 1; // Set trigonometric coefficients for forward transform if (sign == -1) { w_real = wavetable[twiddle].real(); w_imag = wavetable[twiddle].imag(); } // ... otherwise set trigonometric coefficients for backward // tranform: w -> conjugate(w) else { w_real = wavetable[twiddle].real(); w_imag = -wavetable[twiddle].imag(); } } // Loop over ... for (int i = 0; i < m; ++i) { // const double xp_real = (out+ostride*(i + e1 * m))->real(); const double xp_imag = (out+ostride*(i + e1 * m))->imag(); const double xm_real = (out+ostride*(i + (factor-e1)*m))->real(); const double xm_imag = (out+ostride*(i + (factor-e1)*m))->imag(); // const double ap = w_real * xp_real; const double am = w_imag * xm_imag; // double sum_real = ap - am; double sumc_real = ap + am; // const double bp = w_real * xp_imag; const double bm = w_imag * xm_real; // double sum_imag = bp + bm; double sumc_imag = bp - bm; // *(in+istride*(i+em)) = std::complex((in+istride*(i+em))->real() + sum_real, (in+istride*(i+em))->imag() + sum_imag); *(in+istride*(i+ecm)) = std::complex((in+istride*(i+ecm))->real() + sumc_real, (in+istride*(i+ecm))->imag() + sumc_imag); } // endfor: i // Increment idx += idx_step ; idx %= factor * q ; } // endfor: e1 } // endfor: e // k = 0 for (int k1 = 0; k1 < p_1; ++k1) { *(out+ostride*k1) = *(in+istride*k1); } // k > 0 for (int e1 = 1; e1 < factor; ++e1) { for (int k1 = 0; k1 < p_1; ++k1) { *(out+ostride*(k1 + e1 * p_1)) = *(in+istride*(k1 + e1 * m)); } } // e = 0 for (int k = 1, i = p_1, j = product; k < q; ++k, j += jump) { for (int k1 = 0; k1 < p_1; ++k1, ++i, ++j) { *(out+ostride*j) = *(in+istride*i); } } // e > 0 for (int k = 1, i = p_1, j = product; k < q; ++k, j += jump) { for (int k1 = 0; k1 < p_1; ++k1, ++i, ++j) { for (int e1 = 1; e1 < factor; ++e1) { // Get x std::complex x = *(in+istride*(i + e1 * m)); // Compute index int twiddle = index + (e1-1)*q + k-1; // Get w std::complex w = (sign == -1) ? wavetable[twiddle] : std::conj(wavetable[twiddle]); // Compute out = w * x *(out+ostride*(j + e1 * p_1)) = w * x; } // endfor: e1 } // endfor: k1 } // endfor: k // Return return; } /***********************************************************************//** * @brief Extract coefficients from wavetable * * @param[in] index Start index of trigonometric coefficients. * @param[in] k ... * @param[in] q ... * @param[in] n Number of coefficients. * @param[in] sign Forward (-1) or backward (+1) transformation. * @param[in] wavetable Trigonometric coefficients. ***************************************************************************/ std::vector > GFft::get_w(const GFftWavetable& wavetable, const int& index, const int& k, const int& q, const int& n, const int& sign) const { // Allocate w vectors std::vector > w(n); // Set trigonometric coefficients for k=0 since they are not stored in the // wavetable object if (k == 0) { w.assign(n, std::complex(1.0, 0.0)); } // ... otherwise use coefficients stored in wavetable else { // Compute indices int twiddle = index + k - 1; // If sign==-1 then set trigonometric coefficients for forward transform if (sign == -1) { for (int i = 0; i < n; ++i, twiddle += q) { w[i] = wavetable[twiddle]; } } // ... otherwise set trigonometric coefficients for backward tranform // w -> conjugate(w) else { for (int i = 0; i < n; ++i, twiddle += q) { w[i] = std::conj(wavetable[twiddle]); } } } // endelse: k != 0 // Return w vectors return w; }