/////////  CZT and ICZT  Chirp Z-transform Source Code  ////////////////////////
///
///  gcc -O3 -Wall -W -Wextra -o czt czt.c -lm
//
//
////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <math.h>

#include <complex.h>

#undef  PI
#define PI  3.1415926535897932384626433832795L  // 円周率(π)

#define Rotor(a)      (double complex)( cos(a) + I*sin(a) )
#define COMPLEX(a,b)  (double complex)( (a) + I*(b) )

enum { DFT, IDFT };
enum { FFT=DFT, IFFT=IDFT };
enum { CZT=DFT, ICZT=IDFT };

////////////////////////////////////////////////////////////////////////////////
////
//// CZT Chirp Z-transform, Bluestein's algorithm
////
//// Structure to store data for CZT Chirp Z-transform
////  samples    : number of actual data
////  samples_out: Actual data count (for output, same as input one)
////  samples_fft: Number of data used in CZT conversion (power of 2)
////  flag_fft   : flag indicating if "samples" is a power of 2
////  w          : The number of weight data elements is (samples)
////  v          : The number of impulse response data elements is (samples_fft)
////  
typedef struct CZT_CONTROL {
    int  samples;      // 標本点の数
    int  samples_out;  // 出力する標本点の数(基本入力標本点数)
    int  samples_fft;  // 2の整数乗値:flag_fft?samples:2*get_czt_size(samples)
    int  flag_fft;     // 直接FFTを実行できるサイズかの判定
    double complex *w; // 重みデータ            要素数は(samples)
    double complex *v; // インパルス応答データ   要素数は(samples_fft)
} CZT_CONTROL;

////////////////////////////////////////////////////////////////////////////////
//// checks if the argument is a power of 2 ( Returns 1 if it is a power of 2 )
////
int ft_size_check( int n ){ if( n<4 ) return 0;
    for(int e=n;e>1;e>>=1 ) if( e&1 ) return 0;
    return 1;
}

////////////////////////////////////////////////////////////////////////////////
//// A function that returns the smallest power of 2 that is greater than or 
//// equal to twice its argument
////
int get_czt_size( int n ){
    if( ft_size_check(n) ) return n;
    int a=1, b= n<<1;
    for( a=1; a<b; a<<=1 );
    return a;
}

////////////////////////////////////////////////////////////////////////////////
//// FFT routine for simple one-dimensional data
////     double complex *c: Pointer to complex array for input/output
////     elements  : number of data in complex array
////     flag      : Flag indicating forward transform or inverse transform
////                 { FFT | IFFT }
////
void fft( double complex *c, int elements, int flag ){
    if( !ft_size_check(elements) ) return; //2の冪(べき)のみ、power of two, ONLY
    double complex *rotor = (double complex *)calloc( elements, sizeof(double complex) );
    if(!rotor) return; // Processing is aborted because the twiddle factor table
                       // used for calculation could not be secured.
    double  unit = (flag==IFFT) ? 2*M_PI/elements : -2*M_PI/elements;;
    for(int e=0; e<elements; rotor[e]=Rotor(unit*e), e++ );
    
    for(int i=0, j=1; j<elements-1; j++ ) {// scrambler, for initial placement
        for(int k=elements>>1; k>(i^=k); k>>=1 );
        if( j<i ){double complex temp=c[j]; c[j]=c[i], c[i]=temp; }  // exchange
    }
    for(int m, mh=1; (m=mh<<1)<=elements; mh=m ) {// Operations
        for(int ir=0, i=0; i<elements; i+=m ) {   // ir=0: Rotation angle is 0rad.
            register double complex temp, W=rotor[ir];   // W: Rotation factor
            for(int k=elements>>2; k>(ir^=k); k>>=1 );
            for(int j=i; j<mh+i; j++ ) { int a = j,  b = j + mh;
                temp = c[a] - c[b];  // For butterfly operation
                c[a] = c[a] + c[b];  // a ->- (a + b) ->- a
                c[b] = temp * W;     // b ->- (a-b)*W ->- b
    }   }   }   double reciprocal = 1./elements;
    if(flag==IFFT)for(int i=0; i<elements; c[i++]*=reciprocal );
    free(rotor);
}

////////////////////////////////////////////////////////////////////////////////
//// A function that generates a structure for Chirp Z-transform for a given 
//// integer value and returns its pointer.
//// You MUST call "free()" with this pointer when it is no longer needed.
////
CZT_CONTROL *GenerateCZT_CONTROL( int n ){
    int samples     = n;
    int samples_out = n;
    int samples_fft = ft_size_check(n) ? n : get_czt_size(n);
    int flag_fft    = ft_size_check(n);
    CZT_CONTROL  *a = (CZT_CONTROL*)malloc( sizeof(CZT_CONTROL) + sizeof(double complex) *
                                          (samples + (samples_fft +1)) );  // +1 for dummy
    if( !a ) return NULL;
    
    *a = (CZT_CONTROL){
            .samples     = samples,     // 標本点の数
            .samples_out = samples_out, // 出力する標本点の数
            .samples_fft = samples_fft, // 2の冪:2の整数乗値
            .flag_fft    = flag_fft,    // samples が直接FFTを実行できるサイズかの判定
            .w           = (double complex *)&a[1],       // 重みデータ          要素数は(samples)
            .v           = &((double complex *)&a[1])[n], // インパルス応答データ 要素数は(samples_fft) +dummy
           };
    double rad = PI/n;   double complex *w=a->w, *v=a->v;  // as alias
    for(int i=n;i<samples_fft;i++) v[i]=COMPLEX(0,0);
    for(int tail=samples_fft, n2=n<<1, j=0, i=0;i<n;i++, j=(j+(2*i-1))%n2 ){
        double c=cos(rad*j), s=sin(rad*j);
        w[i]      = COMPLEX( c, -s ); // complex conjugate:  0<= { w[i] <-> v[i] } < n
        v[i]      = COMPLEX( c,  s );
        v[tail-i] = COMPLEX( c,  s ); // v[tail] was keeded as dummy area
    }   fft( v, samples_fft, FFT );
    return a;
}

////////////////////////////////////////////////////////////////////////////////
//// Chirp Z-transform routine for fast Fourier transform of arbitrary size data
//// Argument structure is the same as the FFT routine
////
void czt( double complex *c, int n, int flag ){//For One Dimension data processing
    if( ft_size_check(n) ) return fft( c, n, flag );
    CZT_CONTROL    *czt = NULL;
    double complex *tmp = NULL;   // Temporary work area used by CZT

    if( !(czt = GenerateCZT_CONTROL( n ) ) )                       goto GOOD_BY;
    int    nx = czt->samples_fft, dc_size = sizeof(double complex);
    double complex *w = czt->w;
    double complex *v = czt->v;
    
    if( !(tmp = (double complex*)calloc( nx, dc_size )) )          goto GOOD_BY;

    // Division is slow, so we replace it with multiplication using the reciprocal.
    double   m = 1.0/n;  // a*m, same as a/n. if m is m = 1.0/n;

    //for(int i=n;i<nx;tmp[i++]=0+I*0);    // from n to nx are already zero.
    if( flag==ICZT ) { // 逆変換
      // ICZT part: Inverse transform processing part, inverse transform can be 
      // easily realized by using complex conjugate numbers.
      for(int i=0;i< n;i++) tmp[i] =   c[i] * conj(w[i]);
      fft( tmp, nx,  FFT );
      for(int i=0;i<nx;i++) tmp[i] = tmp[i] * conj(v[i]);
      fft( tmp, nx, IFFT );
      for(int i=0;i< n;i++)   c[i] = tmp[i] * conj(w[i]) * m;
      // Note: For the inverse transform, divide the result after the transform 
      // process by the number of elements.
    } else {// flag==CZT  順変換
      // CZT part: Forward transform processing unit
      for(int i=0;i< n;i++) tmp[i] =   c[i] * w[i]; // weight data multiplication
      fft( tmp, nx,  FFT );                         // Fourier transform
      for(int i=0;i<nx;i++) tmp[i] = tmp[i] * v[i]; // convolution
      fft( tmp, nx, IFFT );                         // inverse Fourier transform                                
      for(int i=0;i< n;i++)   c[i] = tmp[i] * w[i]; // weight data multiplication
    }

  GOOD_BY:;
    free( tmp );
    free( czt );
}



////////////////////////////////////////////////////////////////////////////////
////
//// Sample using CZT
//// 
////////////////////////////////////////////////////////////////////////////////
int NumberOfData=19, quiet=0, verbose=0;

#include <string.h>
void argumentAnalysis(int argc, char *argv[]){ //, char **iname, char **oname){
  int  i, st=1;
    for( i=1; i<argc; i++ ) {
        if( argv[i][0]=='-' ) {
            switch(argv[i][1]) {
                case 'v': verbose=1;     break;
                case 'q': quiet=1;       break;
                case 'N': NumberOfData = atoi(&argv[i][2]);
                          if( NumberOfData<1 ) goto GOOD_BY;
                          break;
                case 'h': st=0;
                default : goto GOOD_BY;
            }
        }
        else goto GOOD_BY;
    }
    return;
  GOOD_BY:;
    char *p=argv[0], *op="[-N{Integer number of data}] [-v] [-q] [-h] ";
    for( i=strlen(p); i>0 && p[i]!='\\' && p[i]!='/'; i-- );; if(i) i++;
    fprintf( stderr, "\nusage: %s %s\n\n", &p[i], op );
    fprintf( stderr, "\t-N: The number of data.\n" );
    fprintf( stderr, "\t-v: verbose mode.\n" );
    fprintf( stderr, "\t-q: quiet mode.\n" );
    fprintf( stderr, "\t-h: This option shows only this message.\n" );
    exit(st);
}

int main(int argc, char *argv[]){
    argumentAnalysis( argc,  argv );
    int data = NumberOfData;

    double complex *base= (double complex*)malloc( (3*data)*sizeof(double complex) );
    if( !base ) return 1;
    double complex *c1 = &base[0];
    double complex *c2 = &base[data];
    double complex *c3 = &base[data+data];

    for(int i=0;i<data;i++) { // Generating dummy data
        double real = 6 * cos(3 * 2 * PI * i / data) + 4 * sin(7 * 2 * PI * i / data);
        c1[i] = c2[i] = real + I * 0;
    }
    ////////////////////////////////////////////////////////////////////////////

    czt( c2, data,  CZT);              // 順変換: CZT
    
    for(int i=0;i<data;i++) c3[i]=c2[i];
    
    czt( c3, data, ICZT);              // 逆変換: ICZT
    
    ////////////////////////////////////////////////////////////////////////////
    
    printf( "\n          %s              %s          %s\n", 
            "Original data", "Fourier transform", "Inverse transformation");
    
    int is_same = 1;
    for(int i = 0; i < data; i++) {
        if( !quiet )printf("%5d | %11.7f %11.7f | %12.7f %12.7f | %11.7f %11.7f\n",
                           i, creal(c1[i]),cimag(c1[i]), creal(c2[i]),cimag(c2[i]),
                           creal(c3[i]),cimag(c3[i]) );
        if( fabs(creal(c1[i]) - creal(c3[i])) > 0.0000001 ) is_same = 0;
        if( fabs(cimag(c1[i]) - cimag(c3[i])) > 0.0000001 ) is_same = 0;
    }
    char *msg = "The result of comparing the original data and";
    printf( "\n\n %s the inverse transformed data\n is %s.",  msg, 
                              is_same ? "Matched" : "there was a difference" );
    if(verbose) printf("\t\t\t ( % 6d  % 6d )", data, get_czt_size(data) );
    printf("\n\n");

    free(base);
    return EXIT_SUCCESS;
}