• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2020  Dario Mambro ( dario.mambro@gmail.com )
2    Copyright (c) 2020  Hayati Ayguen ( h_ayguen@web.de )
3 
4    Redistribution and use of the Software in source and binary forms,
5    with or without modification, is permitted provided that the
6    following conditions are met:
7 
8    - Neither the names of PFFFT, nor the names of its
9    sponsors or contributors may be used to endorse or promote products
10    derived from this Software without specific prior written permission.
11 
12    - Redistributions of source code must retain the above copyright
13    notices, this list of conditions, and the disclaimer below.
14 
15    - Redistributions in binary form must reproduce the above copyright
16    notice, this list of conditions, and the disclaimer below in the
17    documentation and/or other materials provided with the
18    distribution.
19 
20    THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21    EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
22    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23    NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
24    HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
26    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
28    SOFTWARE.
29 */
30 
31 #pragma once
32 
33 #include <complex>
34 #include <vector>
35 #include <limits>
36 #include <cassert>
37 
38 namespace pffft {
39 namespace detail {
40 #if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
41 #include "pffft.h"
42 #endif
43 #if defined(PFFFT_ENABLE_DOUBLE)
44 #include "pffft_double.h"
45 #endif
46 }
47 }
48 
49 namespace pffft {
50 
51 // enum { PFFFT_REAL, PFFFT_COMPLEX }
52 typedef detail::pffft_transform_t TransformType;
53 
54 // define 'Scalar' and 'Complex' (in namespace pffft) with template Types<>
55 // and other type specific helper functions
56 template<typename T> struct Types {};
57 #if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
58 template<> struct Types<float>  {
59   typedef float  Scalar;
60   typedef std::complex<Scalar> Complex;
simd_sizepffft::Types61   static int simd_size() { return detail::pffft_simd_size(); }
simd_archpffft::Types62   static const char * simd_arch() { return detail::pffft_simd_arch(); }
minFFtsizepffft::Types63   static int minFFtsize() { return pffft_min_fft_size(detail::PFFFT_REAL); }
isValidSizepffft::Types64   static bool isValidSize(int N) { return pffft_is_valid_size(N, detail::PFFFT_REAL); }
nearestTransformSizepffft::Types65   static int nearestTransformSize(int N, bool higher) { return pffft_nearest_transform_size(N, detail::PFFFT_REAL, higher ? 1 : 0); }
66 };
67 template<> struct Types< std::complex<float> >  {
68   typedef float  Scalar;
69   typedef std::complex<float>  Complex;
simd_sizepffft::Types70   static int simd_size() { return detail::pffft_simd_size(); }
simd_archpffft::Types71   static const char * simd_arch() { return detail::pffft_simd_arch(); }
minFFtsizepffft::Types72   static int minFFtsize() { return pffft_min_fft_size(detail::PFFFT_COMPLEX); }
isValidSizepffft::Types73   static bool isValidSize(int N) { return pffft_is_valid_size(N, detail::PFFFT_COMPLEX); }
nearestTransformSizepffft::Types74   static int nearestTransformSize(int N, bool higher) { return pffft_nearest_transform_size(N, detail::PFFFT_COMPLEX, higher ? 1 : 0); }
75 };
76 #endif
77 #if defined(PFFFT_ENABLE_DOUBLE)
78 template<> struct Types<double> {
79   typedef double Scalar;
80   typedef std::complex<Scalar> Complex;
simd_sizepffft::Types81   static int simd_size() { return detail::pffftd_simd_size(); }
simd_archpffft::Types82   static const char * simd_arch() { return detail::pffftd_simd_arch(); }
minFFtsizepffft::Types83   static int minFFtsize() { return pffftd_min_fft_size(detail::PFFFT_REAL); }
isValidSizepffft::Types84   static bool isValidSize(int N) { return pffftd_is_valid_size(N, detail::PFFFT_REAL); }
nearestTransformSizepffft::Types85   static int nearestTransformSize(int N, bool higher) { return pffftd_nearest_transform_size(N, detail::PFFFT_REAL, higher ? 1 : 0); }
86 };
87 template<> struct Types< std::complex<double> > {
88   typedef double Scalar;
89   typedef std::complex<double> Complex;
simd_sizepffft::Types90   static int simd_size() { return detail::pffftd_simd_size(); }
simd_archpffft::Types91   static const char * simd_arch() { return detail::pffftd_simd_arch(); }
minFFtsizepffft::Types92   static int minFFtsize() { return pffftd_min_fft_size(detail::PFFFT_COMPLEX); }
isValidSizepffft::Types93   static bool isValidSize(int N) { return pffftd_is_valid_size(N, detail::PFFFT_COMPLEX); }
nearestTransformSizepffft::Types94   static int nearestTransformSize(int N, bool higher) { return pffftd_nearest_transform_size(N, detail::PFFFT_COMPLEX, higher ? 1 : 0); }
95 };
96 #endif
97 
98 // Allocator
99 template<typename T> class PFAlloc;
100 
101 namespace detail {
102   template<typename T> class Setup;
103 }
104 
105 #if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
106 
107 // define AlignedVector<T> utilizing 'using' in C++11
108 template<typename T>
109 using AlignedVector = typename std::vector< T, PFAlloc<T> >;
110 
111 #else
112 
113 // define AlignedVector<T> having to derive std::vector<>
114 template <typename T>
115 struct AlignedVector : public std::vector< T, PFAlloc<T> > {
AlignedVectorpffft::AlignedVector116   AlignedVector() : std::vector< T, PFAlloc<T> >() { }
AlignedVectorpffft::AlignedVector117   AlignedVector(int N) : std::vector< T, PFAlloc<T> >(N) { }
118 };
119 
120 #endif
121 
122 
123 // T can be float, double, std::complex<float> or std::complex<double>
124 //   define PFFFT_ENABLE_DOUBLE before include this file for double and std::complex<double>
125 template<typename T>
126 class Fft
127 {
128 public:
129 
130   // define types value_type, Scalar and Complex
131   typedef T value_type;
132   typedef typename Types<T>::Scalar  Scalar;
133   typedef typename Types<T>::Complex Complex;
134 
135   // static retrospection functions
isComplexTransform()136   static bool isComplexTransform()  { return sizeof(T) == sizeof(Complex); }
isFloatScalar()137   static bool isFloatScalar()  { return sizeof(Scalar) == sizeof(float); }
isDoubleScalar()138   static bool isDoubleScalar() { return sizeof(Scalar) == sizeof(double); }
139 
140   // simple helper to determine next power of 2 - without inexact/rounding floating point operations
nextPowerOfTwo(int N)141   static int nextPowerOfTwo(int N) { return detail::pffft_next_power_of_two(N); }
isPowerOfTwo(int N)142   static bool isPowerOfTwo(int N) { return detail::pffft_is_power_of_two(N) ? true : false; }
143 
144 
simd_size()145   static int simd_size() { return Types<T>::simd_size(); }
simd_arch()146   static const char * simd_arch() { return Types<T>::simd_arch(); }
147 
148   // simple helper to get minimum possible fft length
minFFtsize()149   static int minFFtsize() { return Types<T>::minFFtsize(); }
150 
151   // helper to determine nearest transform size - factorizable to minFFtsize() with factors 2, 3, 5
isValidSize(int N)152   static bool isValidSize(int N) { return Types<T>::isValidSize(N); }
nearestTransformSize(int N,bool higher=true)153   static int nearestTransformSize(int N, bool higher=true) { return Types<T>::nearestTransformSize(N, higher); }
154 
155 
156   //////////////////
157 
158   /*
159    * Contructor, with transformation length, preparing transforms.
160    *
161    * For length <= stackThresholdLen, the stack is used for the internal
162    * work memory. for bigger length', the heap is used.
163    *
164    * Using the stack is probably the best strategy for small
165    * FFTs, say for N <= 4096). Threads usually have a small stack, that
166    * there's no sufficient amount of memory, usually leading to a crash!
167    */
168   Fft( int length, int stackThresholdLen = 4096 );
169 
170 
171   /*
172    * constructor or prepareLength() produced a valid FFT instance?
173    * delivers false for invalid FFT sizes
174    */
175   bool isValid() const;
176 
177 
178   ~Fft();
179 
180   /*
181    * prepare for transformation length 'newLength'.
182    * length is identical to forward()'s input vector's size,
183    * and also equals inverse()'s output vector size.
184    * this function is no simple setter. it pre-calculates twiddle factors.
185    * returns true if newLength is >= minFFtsize, false otherwise
186    */
187   bool prepareLength(int newLength);
188 
189   /*
190    * retrieve the transformation length.
191    */
getLength() const192   int getLength() const { return length; }
193 
194   /*
195    * retrieve size of complex spectrum vector,
196    * the output of forward()
197    */
getSpectrumSize() const198   int getSpectrumSize() const { return isComplexTransform() ? length : ( length / 2 ); }
199 
200   /*
201    * retrieve size of spectrum vector - in internal layout;
202    * the output of forwardToInternalLayout()
203    */
getInternalLayoutSize() const204   int getInternalLayoutSize() const { return isComplexTransform() ? ( 2 * length ) : length; }
205 
206 
207   ////////////////////////////////////////////
208   ////
209   //// API 1, with std::vector<> based containers,
210   ////   which free the allocated memory themselves (RAII).
211   ////
212   //// uses an Allocator for the alignment of SIMD data.
213   ////
214   ////////////////////////////////////////////
215 
216   // create suitably preallocated aligned vector for one FFT
217   AlignedVector<T>       valueVector() const;
218   AlignedVector<Complex> spectrumVector() const;
219   AlignedVector<Scalar>  internalLayoutVector() const;
220 
221   ////////////////////////////////////////////
222   // although using Vectors for output ..
223   // they need to have resize() applied before!
224 
225   // core API, having the spectrum in canonical order
226 
227   /*
228    * Perform the forward Fourier transform.
229    *
230    * Transforms are not scaled: inverse(forward(x)) = N*x.
231    * Typically you will want to scale the backward transform by 1/N.
232    *
233    * The output 'spectrum' is canonically ordered - as expected.
234    *
235    * a) for complex input isComplexTransform() == true,
236    *    and transformation length N  the output array is complex:
237    *   index k in 0 .. N/2 -1  corresponds to frequency k * Samplerate / N
238    *   index k in N/2 .. N -1  corresponds to frequency (k -N) * Samplerate / N,
239    *     resulting in negative frequencies
240    *
241    * b) for real input isComplexTransform() == false,
242    *    and transformation length N  the output array is 'mostly' complex:
243    *   index k in 1 .. N/2 -1  corresponds to frequency k * Samplerate / N
244    *   index k == 0 is a special case:
245    *     the real() part contains the result for the DC frequency 0,
246    *     the imag() part contains the result for the Nyquist frequency Samplerate/2
247    *   both 0-frequency and half frequency components, which are real,
248    *   are assembled in the first entry as  F(0)+i*F(N/2).
249    *   with the output size N/2 complex values, it is obvious, that the
250    *   result for negative frequencies are not output, cause of symmetry.
251    *
252    * input and output may alias - if you do nasty type conversion.
253    * return is just the given output parameter 'spectrum'.
254    */
255   AlignedVector<Complex> & forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum);
256 
257   /*
258    * Perform the inverse Fourier transform, see forward().
259    * return is just the given output parameter 'output'.
260    */
261   AlignedVector<T> & inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output);
262 
263 
264   // provide additional functions with spectrum in some internal Layout.
265   // these are faster, cause the implementation omits the reordering.
266   // these are useful in special applications, like fast convolution,
267   // where inverse() is following anyway ..
268 
269   /*
270    * Perform the forward Fourier transform - similar to forward(), BUT:
271    *
272    * The z-domain data is stored in the most efficient order
273    * for transforming it back, or using it for convolution.
274    * If you need to have its content sorted in the "usual" canonical order,
275    * either use forward(), or call reorderSpectrum() after calling
276    * forwardToInternalLayout(), and before the backward fft
277    *
278    * return is just the given output parameter 'spectrum_internal_layout'.
279    */
280   AlignedVector<Scalar> & forwardToInternalLayout(
281           const AlignedVector<T> & input,
282           AlignedVector<Scalar> & spectrum_internal_layout );
283 
284   /*
285    * Perform the inverse Fourier transform, see forwardToInternalLayout()
286    *
287    * return is just the given output parameter 'output'.
288    */
289   AlignedVector<T> & inverseFromInternalLayout(
290           const AlignedVector<Scalar> & spectrum_internal_layout,
291           AlignedVector<T> & output );
292 
293   /*
294    * Reorder the spectrum from internal layout to have the
295    * frequency components in the correct "canonical" order.
296    * see forward() for a description of the canonical order.
297    *
298    * input and output should not alias.
299    */
300   void reorderSpectrum(
301           const AlignedVector<Scalar> & input,
302           AlignedVector<Complex> & output );
303 
304   /*
305    * Perform a multiplication of the frequency components of
306    * spectrum_internal_a and spectrum_internal_b
307    * into spectrum_internal_ab.
308    * The arrays should have been obtained with forwardToInternalLayout)
309    * and should *not* have been reordered with reorderSpectrum().
310    *
311    * the operation performed is:
312    *  spectrum_internal_ab = (spectrum_internal_a * spectrum_internal_b)*scaling
313    *
314    * The spectrum_internal_[a][b], pointers may alias.
315    * return is just the given output parameter 'spectrum_internal_ab'.
316    */
317   AlignedVector<Scalar> & convolve(
318           const AlignedVector<Scalar> & spectrum_internal_a,
319           const AlignedVector<Scalar> & spectrum_internal_b,
320           AlignedVector<Scalar> & spectrum_internal_ab,
321           const Scalar scaling );
322 
323   /*
324    * Perform a multiplication and accumulation of the frequency components
325    * - similar to convolve().
326    *
327    * the operation performed is:
328    *  spectrum_internal_ab += (spectrum_internal_a * spectrum_internal_b)*scaling
329    *
330    * The spectrum_internal_[a][b], pointers may alias.
331    * return is just the given output parameter 'spectrum_internal_ab'.
332    */
333   AlignedVector<Scalar> & convolveAccumulate(
334           const AlignedVector<Scalar> & spectrum_internal_a,
335           const AlignedVector<Scalar> & spectrum_internal_b,
336           AlignedVector<Scalar> & spectrum_internal_ab,
337           const Scalar scaling );
338 
339 
340   ////////////////////////////////////////////
341   ////
342   //// API 2, dealing with raw pointers,
343   //// which need to be deallocated using alignedFree()
344   ////
345   //// the special allocation is required cause SIMD
346   //// implementations require aligned memory
347   ////
348   //// Method descriptions are equal to the methods above,
349   //// having  AlignedVector<T> parameters - instead of raw pointers.
350   //// That is why following methods have no documentation.
351   ////
352   ////////////////////////////////////////////
353 
354   static void alignedFree(void* ptr);
355 
356   static T * alignedAllocType(int length);
357   static Scalar* alignedAllocScalar(int length);
358   static Complex* alignedAllocComplex(int length);
359 
360   // core API, having the spectrum in canonical order
361 
362   Complex* forward(const T* input, Complex* spectrum);
363 
364   T* inverse(const Complex* spectrum, T* output);
365 
366 
367   // provide additional functions with spectrum in some internal Layout.
368   // these are faster, cause the implementation omits the reordering.
369   // these are useful in special applications, like fast convolution,
370   // where inverse() is following anyway ..
371 
372   Scalar* forwardToInternalLayout(const T* input,
373                                 Scalar* spectrum_internal_layout);
374 
375   T* inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output);
376 
377   void reorderSpectrum(const Scalar* input, Complex* output );
378 
379   Scalar* convolve(const Scalar* spectrum_internal_a,
380                    const Scalar* spectrum_internal_b,
381                    Scalar* spectrum_internal_ab,
382                    const Scalar scaling);
383 
384   Scalar* convolveAccumulate(const Scalar* spectrum_internal_a,
385                              const Scalar* spectrum_internal_b,
386                              Scalar* spectrum_internal_ab,
387                              const Scalar scaling);
388 
389 private:
390   detail::Setup<T> setup;
391   Scalar* work;
392   int length;
393   int stackThresholdLen;
394 };
395 
396 
397 template<typename T>
alignedAlloc(int length)398 inline T* alignedAlloc(int length) {
399   return (T*)detail::pffft_aligned_malloc( length * sizeof(T) );
400 }
401 
alignedFree(void * ptr)402 inline void alignedFree(void *ptr) {
403     detail::pffft_aligned_free(ptr);
404 }
405 
406 
407 // simple helper to determine next power of 2 - without inexact/rounding floating point operations
nextPowerOfTwo(int N)408 inline int nextPowerOfTwo(int N) {
409   return detail::pffft_next_power_of_two(N);
410 }
411 
isPowerOfTwo(int N)412 inline bool isPowerOfTwo(int N) {
413   return detail::pffft_is_power_of_two(N) ? true : false;
414 }
415 
416 
417 
418 ////////////////////////////////////////////////////////////////////
419 
420 // implementation
421 
422 namespace detail {
423 
424 template<typename T>
425 class Setup
426 {};
427 
428 #if defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) )
429 
430 template<>
431 class Setup<float>
432 {
433   PFFFT_Setup* self;
434 
435 public:
436   typedef float value_type;
437   typedef Types< value_type >::Scalar Scalar;
438 
Setup()439   Setup()
440     : self(NULL)
441   {}
442 
~Setup()443   ~Setup() { pffft_destroy_setup(self); }
444 
prepareLength(int length)445   void prepareLength(int length)
446   {
447     if (self) {
448       pffft_destroy_setup(self);
449     }
450     self = pffft_new_setup(length, PFFFT_REAL);
451   }
452 
isValid() const453   bool isValid() const { return (self); }
454 
transform_ordered(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)455   void transform_ordered(const Scalar* input,
456                          Scalar* output,
457                          Scalar* work,
458                          pffft_direction_t direction)
459   {
460     pffft_transform_ordered(self, input, output, work, direction);
461   }
462 
transform(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)463   void transform(const Scalar* input,
464                  Scalar* output,
465                  Scalar* work,
466                  pffft_direction_t direction)
467   {
468     pffft_transform(self, input, output, work, direction);
469   }
470 
reorder(const Scalar * input,Scalar * output,pffft_direction_t direction)471   void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
472   {
473     pffft_zreorder(self, input, output, direction);
474   }
475 
convolveAccumulate(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)476   void convolveAccumulate(const Scalar* dft_a,
477                           const Scalar* dft_b,
478                           Scalar* dft_ab,
479                           const Scalar scaling)
480   {
481     pffft_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
482   }
483 
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)484   void convolve(const Scalar* dft_a,
485                 const Scalar* dft_b,
486                 Scalar* dft_ab,
487                 const Scalar scaling)
488   {
489     pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
490   }
491 };
492 
493 
494 template<>
495 class Setup< std::complex<float> >
496 {
497   PFFFT_Setup* self;
498 
499 public:
500   typedef std::complex<float> value_type;
501   typedef Types< value_type >::Scalar Scalar;
502 
Setup()503   Setup()
504     : self(NULL)
505   {}
506 
~Setup()507   ~Setup() { pffft_destroy_setup(self); }
508 
prepareLength(int length)509   void prepareLength(int length)
510   {
511     if (self) {
512       pffft_destroy_setup(self);
513     }
514     self = pffft_new_setup(length, PFFFT_COMPLEX);
515   }
516 
isValid() const517   bool isValid() const { return (self); }
518 
transform_ordered(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)519   void transform_ordered(const Scalar* input,
520                          Scalar* output,
521                          Scalar* work,
522                          pffft_direction_t direction)
523   {
524     pffft_transform_ordered(self, input, output, work, direction);
525   }
526 
transform(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)527   void transform(const Scalar* input,
528                  Scalar* output,
529                  Scalar* work,
530                  pffft_direction_t direction)
531   {
532     pffft_transform(self, input, output, work, direction);
533   }
534 
reorder(const Scalar * input,Scalar * output,pffft_direction_t direction)535   void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
536   {
537     pffft_zreorder(self, input, output, direction);
538   }
539 
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)540   void convolve(const Scalar* dft_a,
541                 const Scalar* dft_b,
542                 Scalar* dft_ab,
543                 const Scalar scaling)
544   {
545     pffft_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
546   }
547 };
548 
549 #endif /* defined(PFFFT_ENABLE_FLOAT) || ( !defined(PFFFT_ENABLE_FLOAT) && !defined(PFFFT_ENABLE_DOUBLE) ) */
550 
551 
552 #if defined(PFFFT_ENABLE_DOUBLE)
553 
554 template<>
555 class Setup<double>
556 {
557   PFFFTD_Setup* self;
558 
559 public:
560   typedef double value_type;
561   typedef Types< value_type >::Scalar Scalar;
562 
Setup()563   Setup()
564     : self(NULL)
565   {}
566 
~Setup()567   ~Setup() { pffftd_destroy_setup(self); }
568 
prepareLength(int length)569   void prepareLength(int length)
570   {
571     if (self) {
572       pffftd_destroy_setup(self);
573       self = NULL;
574     }
575     if (length > 0) {
576       self = pffftd_new_setup(length, PFFFT_REAL);
577     }
578   }
579 
isValid() const580   bool isValid() const { return (self); }
581 
transform_ordered(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)582   void transform_ordered(const Scalar* input,
583                          Scalar* output,
584                          Scalar* work,
585                          pffft_direction_t direction)
586   {
587     pffftd_transform_ordered(self, input, output, work, direction);
588   }
589 
transform(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)590   void transform(const Scalar* input,
591                  Scalar* output,
592                  Scalar* work,
593                  pffft_direction_t direction)
594   {
595     pffftd_transform(self, input, output, work, direction);
596   }
597 
reorder(const Scalar * input,Scalar * output,pffft_direction_t direction)598   void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
599   {
600     pffftd_zreorder(self, input, output, direction);
601   }
602 
convolveAccumulate(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)603   void convolveAccumulate(const Scalar* dft_a,
604                           const Scalar* dft_b,
605                           Scalar* dft_ab,
606                           const Scalar scaling)
607   {
608     pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
609   }
610 
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)611   void convolve(const Scalar* dft_a,
612                 const Scalar* dft_b,
613                 Scalar* dft_ab,
614                 const Scalar scaling)
615   {
616     pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
617   }
618 };
619 
620 template<>
621 class Setup< std::complex<double> >
622 {
623   PFFFTD_Setup* self;
624 
625 public:
626   typedef std::complex<double> value_type;
627   typedef Types< value_type >::Scalar Scalar;
628 
Setup()629   Setup()
630     : self(NULL)
631   {}
632 
~Setup()633   ~Setup() { pffftd_destroy_setup(self); }
634 
prepareLength(int length)635   void prepareLength(int length)
636   {
637     if (self) {
638       pffftd_destroy_setup(self);
639     }
640     self = pffftd_new_setup(length, PFFFT_COMPLEX);
641   }
642 
isValid() const643   bool isValid() const { return (self); }
644 
transform_ordered(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)645   void transform_ordered(const Scalar* input,
646                          Scalar* output,
647                          Scalar* work,
648                          pffft_direction_t direction)
649   {
650     pffftd_transform_ordered(self, input, output, work, direction);
651   }
652 
transform(const Scalar * input,Scalar * output,Scalar * work,pffft_direction_t direction)653   void transform(const Scalar* input,
654                  Scalar* output,
655                  Scalar* work,
656                  pffft_direction_t direction)
657   {
658     pffftd_transform(self, input, output, work, direction);
659   }
660 
reorder(const Scalar * input,Scalar * output,pffft_direction_t direction)661   void reorder(const Scalar* input, Scalar* output, pffft_direction_t direction)
662   {
663     pffftd_zreorder(self, input, output, direction);
664   }
665 
convolveAccumulate(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)666   void convolveAccumulate(const Scalar* dft_a,
667                           const Scalar* dft_b,
668                           Scalar* dft_ab,
669                           const Scalar scaling)
670   {
671     pffftd_zconvolve_accumulate(self, dft_a, dft_b, dft_ab, scaling);
672   }
673 
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)674   void convolve(const Scalar* dft_a,
675                 const Scalar* dft_b,
676                 Scalar* dft_ab,
677                 const Scalar scaling)
678   {
679     pffftd_zconvolve_no_accu(self, dft_a, dft_b, dft_ab, scaling);
680   }
681 };
682 
683 #endif /* defined(PFFFT_ENABLE_DOUBLE) */
684 
685 } // end of anonymous namespace for Setup<>
686 
687 
688 template<typename T>
Fft(int length,int stackThresholdLen)689 inline Fft<T>::Fft(int length, int stackThresholdLen)
690   : work(NULL)
691   , length(0)
692   , stackThresholdLen(stackThresholdLen)
693 {
694 #if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900))
695   static_assert( sizeof(Complex) == 2 * sizeof(Scalar), "pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)" );
696 #elif defined(__GNUC__)
697   char static_assert_like[(sizeof(Complex) == 2 * sizeof(Scalar)) ? 1 : -1]; // pffft requires sizeof(std::complex<>) == 2 * sizeof(Scalar)
698 #endif
699   prepareLength(length);
700 }
701 
702 template<typename T>
~Fft()703 inline Fft<T>::~Fft()
704 {
705   alignedFree(work);
706 }
707 
708 template<typename T>
709 inline bool
isValid() const710 Fft<T>::isValid() const
711 {
712   return setup.isValid();
713 }
714 
715 template<typename T>
716 inline bool
prepareLength(int newLength)717 Fft<T>::prepareLength(int newLength)
718 {
719   if(newLength < minFFtsize())
720     return false;
721 
722   const bool wasOnHeap = ( work != NULL );
723 
724   const bool useHeap = newLength > stackThresholdLen;
725 
726   if (useHeap == wasOnHeap && newLength == length) {
727     return true;
728   }
729 
730   length = 0;
731 
732   setup.prepareLength(newLength);
733   if (!setup.isValid())
734     return false;
735 
736   length = newLength;
737 
738   if (work) {
739     alignedFree(work);
740     work = NULL;
741   }
742 
743   if (useHeap) {
744     work = reinterpret_cast<Scalar*>( alignedAllocType(length) );
745   }
746 
747   return true;
748 }
749 
750 
751 template<typename T>
752 inline AlignedVector<T>
valueVector() const753 Fft<T>::valueVector() const
754 {
755   return AlignedVector<T>(length);
756 }
757 
758 template<typename T>
759 inline AlignedVector< typename Fft<T>::Complex >
spectrumVector() const760 Fft<T>::spectrumVector() const
761 {
762   return AlignedVector<Complex>( getSpectrumSize() );
763 }
764 
765 template<typename T>
766 inline AlignedVector< typename Fft<T>::Scalar >
internalLayoutVector() const767 Fft<T>::internalLayoutVector() const
768 {
769   return AlignedVector<Scalar>( getInternalLayoutSize() );
770 }
771 
772 
773 template<typename T>
774 inline AlignedVector< typename Fft<T>::Complex > &
forward(const AlignedVector<T> & input,AlignedVector<Complex> & spectrum)775 Fft<T>::forward(const AlignedVector<T> & input, AlignedVector<Complex> & spectrum)
776 {
777   forward( input.data(), spectrum.data() );
778   return spectrum;
779 }
780 
781 template<typename T>
782 inline AlignedVector<T> &
inverse(const AlignedVector<Complex> & spectrum,AlignedVector<T> & output)783 Fft<T>::inverse(const AlignedVector<Complex> & spectrum, AlignedVector<T> & output)
784 {
785   inverse( spectrum.data(), output.data() );
786   return output;
787 }
788 
789 
790 template<typename T>
791 inline AlignedVector< typename Fft<T>::Scalar > &
forwardToInternalLayout(const AlignedVector<T> & input,AlignedVector<Scalar> & spectrum_internal_layout)792 Fft<T>::forwardToInternalLayout(
793     const AlignedVector<T> & input,
794     AlignedVector<Scalar> & spectrum_internal_layout )
795 {
796   forwardToInternalLayout( input.data(), spectrum_internal_layout.data() );
797   return spectrum_internal_layout;
798 }
799 
800 template<typename T>
801 inline AlignedVector<T> &
inverseFromInternalLayout(const AlignedVector<Scalar> & spectrum_internal_layout,AlignedVector<T> & output)802 Fft<T>::inverseFromInternalLayout(
803     const AlignedVector<Scalar> & spectrum_internal_layout,
804     AlignedVector<T> & output )
805 {
806   inverseFromInternalLayout( spectrum_internal_layout.data(), output.data() );
807   return output;
808 }
809 
810 template<typename T>
811 inline void
reorderSpectrum(const AlignedVector<Scalar> & input,AlignedVector<Complex> & output)812 Fft<T>::reorderSpectrum(
813     const AlignedVector<Scalar> & input,
814     AlignedVector<Complex> & output )
815 {
816   reorderSpectrum( input.data(), output.data() );
817 }
818 
819 template<typename T>
820 inline AlignedVector< typename Fft<T>::Scalar > &
convolveAccumulate(const AlignedVector<Scalar> & spectrum_internal_a,const AlignedVector<Scalar> & spectrum_internal_b,AlignedVector<Scalar> & spectrum_internal_ab,const Scalar scaling)821 Fft<T>::convolveAccumulate(
822     const AlignedVector<Scalar> & spectrum_internal_a,
823     const AlignedVector<Scalar> & spectrum_internal_b,
824     AlignedVector<Scalar> & spectrum_internal_ab,
825     const Scalar scaling )
826 {
827   convolveAccumulate( spectrum_internal_a.data(), spectrum_internal_b.data(),
828                       spectrum_internal_ab.data(), scaling );
829   return spectrum_internal_ab;
830 }
831 
832 template<typename T>
833 inline AlignedVector< typename Fft<T>::Scalar > &
convolve(const AlignedVector<Scalar> & spectrum_internal_a,const AlignedVector<Scalar> & spectrum_internal_b,AlignedVector<Scalar> & spectrum_internal_ab,const Scalar scaling)834 Fft<T>::convolve(
835     const AlignedVector<Scalar> & spectrum_internal_a,
836     const AlignedVector<Scalar> & spectrum_internal_b,
837     AlignedVector<Scalar> & spectrum_internal_ab,
838     const Scalar scaling )
839 {
840   convolve( spectrum_internal_a.data(), spectrum_internal_b.data(),
841             spectrum_internal_ab.data(), scaling );
842   return spectrum_internal_ab;
843 }
844 
845 
846 template<typename T>
847 inline typename Fft<T>::Complex *
forward(const T * input,Complex * spectrum)848 Fft<T>::forward(const T* input, Complex * spectrum)
849 {
850   assert(isValid());
851   setup.transform_ordered(reinterpret_cast<const Scalar*>(input),
852                           reinterpret_cast<Scalar*>(spectrum),
853                           work,
854                           detail::PFFFT_FORWARD);
855   return spectrum;
856 }
857 
858 template<typename T>
859 inline T*
inverse(Complex const * spectrum,T * output)860 Fft<T>::inverse(Complex const* spectrum, T* output)
861 {
862   assert(isValid());
863   setup.transform_ordered(reinterpret_cast<const Scalar*>(spectrum),
864                           reinterpret_cast<Scalar*>(output),
865                           work,
866                           detail::PFFFT_BACKWARD);
867   return output;
868 }
869 
870 template<typename T>
871 inline typename pffft::Fft<T>::Scalar*
forwardToInternalLayout(const T * input,Scalar * spectrum_internal_layout)872 Fft<T>::forwardToInternalLayout(const T* input, Scalar* spectrum_internal_layout)
873 {
874   assert(isValid());
875   setup.transform(reinterpret_cast<const Scalar*>(input),
876                   spectrum_internal_layout,
877                   work,
878                   detail::PFFFT_FORWARD);
879   return spectrum_internal_layout;
880 }
881 
882 template<typename T>
883 inline T*
inverseFromInternalLayout(const Scalar * spectrum_internal_layout,T * output)884 Fft<T>::inverseFromInternalLayout(const Scalar* spectrum_internal_layout, T* output)
885 {
886   assert(isValid());
887   setup.transform(spectrum_internal_layout,
888                   reinterpret_cast<Scalar*>(output),
889                   work,
890                   detail::PFFFT_BACKWARD);
891   return output;
892 }
893 
894 template<typename T>
895 inline void
reorderSpectrum(const Scalar * input,Complex * output)896 Fft<T>::reorderSpectrum( const Scalar* input, Complex* output )
897 {
898   assert(isValid());
899   setup.reorder(input, reinterpret_cast<Scalar*>(output), detail::PFFFT_FORWARD);
900 }
901 
902 template<typename T>
903 inline typename pffft::Fft<T>::Scalar*
convolveAccumulate(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)904 Fft<T>::convolveAccumulate(const Scalar* dft_a,
905                            const Scalar* dft_b,
906                            Scalar* dft_ab,
907                            const Scalar scaling)
908 {
909   assert(isValid());
910   setup.convolveAccumulate(dft_a, dft_b, dft_ab, scaling);
911   return dft_ab;
912 }
913 
914 template<typename T>
915 inline typename pffft::Fft<T>::Scalar*
convolve(const Scalar * dft_a,const Scalar * dft_b,Scalar * dft_ab,const Scalar scaling)916 Fft<T>::convolve(const Scalar* dft_a,
917                  const Scalar* dft_b,
918                  Scalar* dft_ab,
919                  const Scalar scaling)
920 {
921   assert(isValid());
922   setup.convolve(dft_a, dft_b, dft_ab, scaling);
923   return dft_ab;
924 }
925 
926 template<typename T>
927 inline void
alignedFree(void * ptr)928 Fft<T>::alignedFree(void* ptr)
929 {
930   pffft::alignedFree(ptr);
931 }
932 
933 
934 template<typename T>
935 inline T*
alignedAllocType(int length)936 pffft::Fft<T>::alignedAllocType(int length)
937 {
938   return alignedAlloc<T>(length);
939 }
940 
941 template<typename T>
942 inline typename pffft::Fft<T>::Scalar*
alignedAllocScalar(int length)943 pffft::Fft<T>::alignedAllocScalar(int length)
944 {
945   return alignedAlloc<Scalar>(length);
946 }
947 
948 template<typename T>
949 inline typename Fft<T>::Complex *
alignedAllocComplex(int length)950 Fft<T>::alignedAllocComplex(int length)
951 {
952   return alignedAlloc<Complex>(length);
953 }
954 
955 
956 
957 ////////////////////////////////////////////////////////////////////
958 
959 // Allocator - for std::vector<>:
960 // origin: http://www.josuttis.com/cppcode/allocator.html
961 // http://www.josuttis.com/cppcode/myalloc.hpp
962 //
963 // minor renaming and utilizing of pffft (de)allocation functions
964 // are applied to Jossutis' allocator
965 
966 /* The following code example is taken from the book
967  * "The C++ Standard Library - A Tutorial and Reference"
968  * by Nicolai M. Josuttis, Addison-Wesley, 1999
969  *
970  * (C) Copyright Nicolai M. Josuttis 1999.
971  * Permission to copy, use, modify, sell and distribute this software
972  * is granted provided this copyright notice appears in all copies.
973  * This software is provided "as is" without express or implied
974  * warranty, and with no claim as to its suitability for any purpose.
975  */
976 
977 template <class T>
978 class PFAlloc {
979   public:
980     // type definitions
981     typedef T        value_type;
982     typedef T*       pointer;
983     typedef const T* const_pointer;
984     typedef T&       reference;
985     typedef const T& const_reference;
986     typedef std::size_t    size_type;
987     typedef std::ptrdiff_t difference_type;
988 
989     // rebind allocator to type U
990     template <class U>
991     struct rebind {
992         typedef PFAlloc<U> other;
993     };
994 
995     // return address of values
address(reference value) const996     pointer address (reference value) const {
997         return &value;
998     }
address(const_reference value) const999     const_pointer address (const_reference value) const {
1000         return &value;
1001     }
1002 
1003     /* constructors and destructor
1004      * - nothing to do because the allocator has no state
1005      */
PFAlloc()1006     PFAlloc() throw() {
1007     }
PFAlloc(const PFAlloc &)1008     PFAlloc(const PFAlloc&) throw() {
1009     }
1010     template <class U>
PFAlloc(const PFAlloc<U> &)1011       PFAlloc (const PFAlloc<U>&) throw() {
1012     }
~PFAlloc()1013     ~PFAlloc() throw() {
1014     }
1015 
1016     // return maximum number of elements that can be allocated
max_size() const1017     size_type max_size () const throw() {
1018         return std::numeric_limits<std::size_t>::max() / sizeof(T);
1019     }
1020 
1021     // allocate but don't initialize num elements of type T
allocate(size_type num,const void * =0)1022     pointer allocate (size_type num, const void* = 0) {
1023         pointer ret = (pointer)( alignedAlloc<T>(int(num)) );
1024         return ret;
1025     }
1026 
1027     // initialize elements of allocated storage p with value value
construct(pointer p,const T & value)1028     void construct (pointer p, const T& value) {
1029         // initialize memory with placement new
1030         new((void*)p)T(value);
1031     }
1032 
1033     // destroy elements of initialized storage p
destroy(pointer p)1034     void destroy (pointer p) {
1035         // destroy objects by calling their destructor
1036         p->~T();
1037     }
1038 
1039     // deallocate storage p of deleted elements
deallocate(pointer p,size_type num)1040     void deallocate (pointer p, size_type num) {
1041         // deallocate memory with pffft
1042         alignedFree( (void*)p );
1043     }
1044 };
1045 
1046 // return that all specializations of this allocator are interchangeable
1047 template <class T1, class T2>
operator ==(const PFAlloc<T1> &,const PFAlloc<T2> &)1048 bool operator== (const PFAlloc<T1>&,
1049                  const PFAlloc<T2>&) throw() {
1050     return true;
1051 }
1052 template <class T1, class T2>
operator !=(const PFAlloc<T1> &,const PFAlloc<T2> &)1053 bool operator!= (const PFAlloc<T1>&,
1054                  const PFAlloc<T2>&) throw() {
1055     return false;
1056 }
1057 
1058 
1059 } // namespace pffft
1060 
1061