1/*=============================================================================
  2|
  3| NAME
  4|  
  5|      testFFT.cpp
  6|
  7| DESCRIPTION
  8|  
  9|      Test program for FFT.cpp
 10|    
 11| AUTHOR
 12|
 13|      Sean O'Connor
 14|
 15| LEGAL
 16|
 17|     FFT Version 1.6 - An FFT utility library in C++.
 18|     Copyright (C) 2005-2024 by Sean Erik O'Connor.  All Rights Reserved.
 19|
 20|     This program is free software: you can redistribute it and/or modify
 21|     it under the terms of the GNU General Public License as published by
 22|     the Free Software Foundation, either version 3 of the License, or
 23|     (at your option) any later version.
 24|
 25|     This program is distributed in the hope that it will be useful,
 26|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 27|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 28|     GNU General Public License for more details.
 29|
 30|     You should have received a copy of the GNU General Public License
 31|     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 32|     
 33|     The author's address is seanerikoconnor!AT!gmail!DOT!com
 34|     with !DOT! replaced by . and the !AT! replaced by @
 35|     
 36+============================================================================*/
 37
 38/*------------------------------------------------------------------------------
 39|                                Include Files                                 |
 40------------------------------------------------------------------------------*/
 41
 42#include <iostream>     // Basic stream I/O.
 43#include <cmath>        // Basic math functions.
 44#include <complex>      // Complex data type and operations.
 45#include <vector>       // STL vector class.
 46#include <fstream>      // File stream I/O.
 47#include <algorithm>    // Iterators.
 48#include <string>       // STL string class.
 49#include <stdexcept>    // Exceptions.
 50#include <limits>       // Numeric limits, e.g. floating point.
 51#include <iomanip>      // Floating point formating.
 52
 53using namespace std ;   // Let us omit the std:: prefix for every STL class.
 54using namespace std::complex_literals ;  // Let's us do complex c = 1 +2i 
 55
 56#include "FFT.hpp"      // Our own FFT stuff.
 57
 58// Pick just one.
 59#define SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE
 60//#define COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE
 61//#define HARDCODED_DATA
 62
 63string legalNotice
 64(
 65    "\n"
 66    "FFT Version 1.2 - An FFT utility in C++.\n"
 67    "Copyright (C) 2005-2024 by Sean Erik O'Connor.  All Rights Reserved.\n"
 68   "\n"
 69    "FFT comes with ABSOLUTELY NO WARRANTY; for details see the\n"
 70    "GNU General Public License.  This is free software, and you are welcome\n"
 71    "to redistribute it under certain conditions; see the GNU General Public License\n"
 72    "for details.\n\n"
 73) ;
 74
 75
 76/*=============================================================================
 77|
 78| NAME
 79|  
 80|     main
 81|
 82| DESCRIPTION
 83|
 84|     This program tests the FFT implementation by performing forward and
 85|     inverse transforms on the data and testing error conditions.
 86|    
 87| INPUT
 88|   
 89|     File containing 
 90|        
 91|       - Number of complex points in the transform.
 92|       - The data points (complex numbers).
 93|
 94|    OUTPUT
 95|    
 96|       - DFT of the input.
 97|       - inverse DFT of the first DFT.
 98|    
 99|    EXAMPLE
100|
101|        From the command line, call
102|
103|        $  FastFourierTransform fftIn.txt fftOut.txt
104| 
105|        where the input file fftIn.txt contains
106|
107|        4
108|        (1, 1)
109|        (2, 2)
110|        (3, 3)
111|        (4, 4)
112|     
113|        and the output file fftOut.txt contains
114|
115|        num_points = 4
116|        Point num. 0= (1,1)
117|        Point num. 1= (2,2)
118|        Point num. 2= (3,3)
119|        Point num. 3= (4,4)
120|        The transform of the input data is . . . 
121|        
122|        Point num. 0 =  (5,5)
123|        Point num. 1 =  (-2,-1.11022e-016)
124|        Point num. 2 =  (-1,-1)
125|        Point num. 3 =  (0,-2)
126|        The inverse transform of the transform above is . . .
127|        
128|        Point num. 0 = (1,1)
129|        Point num. 1 = (2,2)
130|        Point num. 2 = (3,3)
131|        Point num. 3 = (4,4)
132|        Point num. 0 = (1,1)
133|        Point num. 1 = (2,2)
134|        Point num. 2 = (3,3)
135|        Point num. 3 = (4,4)
136|
137|        Calling FFT with too many points.
138|        FFT has too many points for its sin/cos table
139|
140+============================================================================*/
141
142int main( int argc, char * argv[] )
143{
144    cout << legalNotice ;
145
146    int num_points = 0 ;   // Number of points in the transform.
147    int i = 0 ;
148
149    // Open file streams for input and output getting file names
150    // from the command line.
151    if (argc != 3)
152    {
153        cerr << "Error:  Missing the names of the input and output "
154                 "files or too many files on the command line." ;
155        return 1 ;
156    }
157
158    char * inputFileName  = argv[ 1 ] ;
159    char * outputFileName = argv[ 2 ] ;
160
161    fstream fin ( inputFileName,  ios::in  ) ;
162    fstream fout( outputFileName, ios::out ) ;
163
164    // Set a really high precision for our printouts.
165    // Of course some binary numbers will have repeating decimals, but we are checking to see if we get the same rounding results
166    // different computer architectures.
167    fout << scientific << setprecision( 100 ) << setw( 100 );
168
169    if (!(fin.is_open() && fout.is_open()))
170    {
171        cerr << "Error:  Problem opening the input or output files. "
172                "Check the file names and permissions." ;
173        return 1 ;
174    }
175
176    try
177    {
178        //////////////////////////////////////////////////////////////////////////
179        /// Single precision FFT
180        //////////////////////////////////////////////////////////////////////////
181
182        // Create our DFT object.
183        FastFourierTransform<float> dft_single ;
184
185        // Clear the fft object.
186        dft_single.clear() ;
187
188        // Read lines until end of file.
189        while (!fin.eof())
190        {
191            // Read the number of points in the transform.
192            fin >> num_points ;
193
194            //  Check to see that the number of points in the FFT is valid.
195            if (num_points <= 0)
196                throw FastFourierTransformException( "The number of points is out of range.\n" ) ;
197
198            // Read in the complex data points from file into the vector.
199            for (i = 0 ;  i < num_points ;  ++i)
200            {
201                complex<float> c ;
202                fin >> c ;
203
204                dft_single.push_back( c ) ;
205            }
206        }
207   
208        fout << "* * * * * * Single precision FFT" << endl ; 
209
210        //  Print out the points just read in.
211        fout << "\n\n\nNumber of points = " << num_points << endl ;
212
213        for (i = 0 ;  i <= num_points - 1 ;  ++i)
214            fout <<  "Point num. " << i << " = " << dft_single[ i ] << endl ;
215
216        // Try a forward FFT.
217        dft_single.fft( true ) ;
218
219        //  Print the transform results.
220        fout << "\n\nThe discrete Fourier transform of the input data is . . . \n\n" ;
221
222        for (i = 0 ;  i <= num_points - 1 ;  ++i)
223            fout << "Point num. " << i << " =  " << dft_single[ i ] << endl ;
224
225        //  Try an inverse transform to get the input data back, with roundoff error.
226        dft_single.fft( false ) ;
227
228        // Print the inverse transform.
229        fout << "\n\nThe inverse transform of the transform above is . . .\n\n" ;
230
231        for (i = 0 ;  i <= num_points - 1 ;  ++i)
232            fout << "Point num. " << i << " = " << dft_single[ i ] << endl ;
233
234        //////////////////////////////////////////////////////////////////////////
235        /// Double precision FFT
236        //////////////////////////////////////////////////////////////////////////
237
238        fout << endl << endl << endl << "* * * * * * Double precision FFT" << endl ; 
239
240        // Create our DFT object.
241        FastFourierTransform<double> dft_double ;
242
243        // Clear the fft object.
244        dft_double.clear() ;
245
246#if defined( SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE ) || defined( COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE )
247        // Rewind the input file to the begining.
248        fin.clear() ;              // Forget we hit the end of file.
249        fin.seekg( 0, ios::beg ) ; // Move to the start of the file.
250#endif // defined( SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE ) || defined( COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE )
251
252#ifdef COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE
253        // Store the lines of file as a vector of strings.
254        vector<string> buffer ;
255        buffer.clear() ;
256
257        // Add each line of the file to the vector.
258        string line_of_file ;
259        while (getline( fin, line_of_file ))
260            buffer.push_back( line_of_file ) ;
261
262        // Now the whole file is in memory.
263        // Parse the first line which is the number of points.
264        istringstream is ;
265        is.clear() ;
266        is.str( buffer[ 0 ] ) ;
267        is >> num_points ;
268
269        //  Check to see that the number of points in the FFT is valid.
270        if (num_points <= 0)
271            throw FastFourierTransformException( "The number of points is out of range.\n" ) ;
272
273        // Now parse all the other lines which are the data points.
274        int line_num = 1 ;
275        while (line_num < buffer.size())
276        {
277            // Get the next line in memory and advance.
278            string line_of_file = buffer[ line_num++ ] ;
279
280            // Parse the complex number.
281            istringstream is ;
282            is.clear() ;
283            is.str( line_of_file ) ;
284            complex<double> c ;
285            is >> c ;
286
287            dft_double.push_back( c ) ;
288        }
289#endif // COMPLICATED_WAY_TO_READ_NUMBERS_FROM_FILE
290
291#ifdef SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE
292        if (!fin.eof())
293        {
294            // Read the number of points in the transform.
295            fin >> num_points ;
296
297            //  Check to see that the number of points in the FFT is valid.
298            if (num_points <= 0)
299            {
300                ostringstream os ;
301                os << "The number of points = " << num_points << " is out of range.\n" ;
302                throw FastFourierTransformException( os.str() ) ;
303            }
304         }
305
306        // Read in the complex data points from file into the vector.
307        for (int index = 1 ;  index <= num_points ;  ++index)
308        {
309            complex<double> c = 0.0 + 0.0i ; // Floating literals default to double.
310    
311            if (!fin.eof())
312               fin >> c ;
313            else
314            {
315                ostringstream os ;
316                os << "The number of data points was too short.\n" ;
317                throw FastFourierTransformException( os.str() ) ;
318            }
319
320            dft_double.push_back( c ) ;
321        }
322#endif // SIMPLE_WAY_TO_READ_NUMBERS_FROM_FILE
323
324#ifdef HARDCODED_DATA
325        num_points = 16 ;
326        dft_double.push_back( 1.0  +  1.0i ) ;
327        dft_double.push_back( 2.0  +  2.0i ) ;
328        dft_double.push_back( 3.0  +  3.0i ) ;
329        dft_double.push_back( 4.0  +  4.0i ) ;
330        dft_double.push_back( 5.0  +  5.0i ) ;
331        dft_double.push_back( 6.0  +  6.0i ) ;
332        dft_double.push_back( 7.0  +  7.0i ) ;
333        dft_double.push_back( 8.0  +  8.0i ) ;
334        dft_double.push_back( 9.0  +  9.0i ) ;
335        dft_double.push_back( 10.0 + 10.0i ) ;
336        dft_double.push_back( 11.0 + 11.0i ) ;
337        dft_double.push_back( 12.0 + 12.0i ) ;
338        dft_double.push_back( 13.0 + 13.0i ) ;
339        dft_double.push_back( 14.0 + 14.0i ) ;
340        dft_double.push_back( 15.0 + 15.0i ) ;
341        dft_double.push_back( 16.0 + 16.0i ) ;
342#endif // HARDCODED_DATA
343
344        //  Print out the points just read in.
345        fout << "\n\n\nNumber of points = " << num_points << endl ;
346
347        for (i = 0 ;  i <= num_points - 1 ;  ++i)
348            fout <<  "Point num. " << i << "= " << dft_double[ i ] << endl ;
349
350        // Try a forward FFT.
351        dft_double.fft( true ) ;
352
353        //  Print the transform results.
354        fout << "\n\nThe discrete Fourier transform of the input data is . . . \n\n" ;
355
356        for (i = 0 ;  i <= num_points - 1 ;  ++i)
357            fout << "Point num. " << i << " =  " << dft_double[ i ] << endl ;
358
359        //  Try an inverse transform to get the input data back, with roundoff error.
360        dft_double.fft( false ) ;
361
362        // Print the inverse transform.
363        fout << "\n\nThe inverse transform of the transform above is . . .\n\n" ;
364
365        for (i = 0 ;  i <= num_points - 1 ;  ++i)
366            fout << "Point num. " << i << " = " << dft_double[ i ] << endl ;
367
368        // =======================================
369        // Test the copy constructor.
370        // =======================================
371
372        fout << "\n\nTest the copy constructor" << endl ;
373    
374        FastFourierTransform copy_of_dft_single( dft_single ) ;
375        for (i = 0 ;  i <= num_points - 1 ;  ++i)
376            fout << "Point num. " << i << " = " << copy_of_dft_single[ i ] << endl ;
377
378        // =======================================
379        // Floating point implementation details.
380        // =======================================
381
382        // Set a really high precision for our printouts.
383        fout << scientific << setprecision( 50 ) << setw( 60 ) << endl ;
384        fout << "= floating point epsilons =" << endl ;
385        fout << "single precision floating point epsilon = " << numeric_limits<float>::epsilon() << endl ;
386        fout << "single precision test: (1.0f + 0.999999f * epsilon) - 1.0f = " << ((1.0f + 0.5f * numeric_limits<float>::epsilon()) - 1.0f) << endl ;
387        fout << "single precision test: (1.0f + epsilon - 1.0f              = " << ((1.0f + numeric_limits<float>::epsilon()) - 1.0f) << endl << endl ;
388
389        fout << "double precision floating point epsilon = " << numeric_limits<double>::epsilon() << endl ;
390        fout << "double precision test: (1.0f + 0.999999 * epsilon) - 1.0f = " << ((1.0f + 0.5f * numeric_limits<double>::epsilon()) - 1.0f) << endl ;
391        fout << "double precision test: (1.0f + epsilon - 1.0f             = " << ((1.0f + numeric_limits<double>::epsilon()) - 1.0f) << endl;
392
393        // =======================================
394        // Test error conditions.  Only the first will execute due to
395        // the throw, so comment out and recompile to test them all.
396        // =======================================
397
398        //  Number of points is less than 2.
399        fout << "\n\nCalling FFT with 1 point.\n" ;
400
401        // Make sure we flush all the data to file, when we're debugging. Then close the files.
402        fout << flush ;
403        fin.close() ;
404        fout.close() ;
405
406        FastFourierTransform<double> dft_single2 ;
407        dft_single2.push_back( complex<double>(1.0, 2.0) ) ;
408        dft_single2.fft( true ) ;
409    }
410
411    // Catch exceptions and print their text explanations.
412    // Clean up resources.
413    catch( FastFourierTransformException & e )
414    {
415        fout << "FFT exception:  " << e.what() << endl ;
416
417        fin.close() ;
418        fout.close() ;
419
420        return 1 ;
421    }
422    catch( runtime_error & e )
423    {
424        fout << "Unexpected runtime exception:  Please email the author." << e.what() << endl ;
425
426        fin.close() ;
427        fout.close() ;
428
429        return 1 ;
430    }
431   catch( ... )
432    {
433        fout << "Unexpected exception:  Please email the author." << endl ;
434
435        fin.close() ;
436        fout.close() ;
437
438        return 1 ;
439    }
440
441
442    return 0 ;
443
444} /* ====================== end of function main ============================ */