#include <../source/TAL/objbase.h>

/*
 * fftwrap.c
 * This module contains both platform-specific wrappers
 * and simple implementation for complex Fast Fourier Transform.
 */

#define	power2(op)	(1<<(op))

#ifdef	HAVE_NSP

#include <../support/nsp/include/nsp.h>
#include <../support/nsp/include/nspfft.h>

void	V_GLOBAL
fftCalcNip(
	mvec_t *ReOut,mvec_t *ImOut,
	mvec_t *ReIn,mvec_t *ImIn,
	unsigned size_power,mvec_t dir_sign)
{
	if(!ReOut || !ImOut || !ReIn || !ImIn || !size_power)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"fftCalcNip(): incorrect parameters.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	if(!ReOut || !ImOut || !ReIn || !ImIn || !size_power)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"fftCalcNip(): incorrect parameters.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	if(dir_sign > 0)	/* Forward FFT */
	{
#ifdef	MATRIX_DOUBLE
		nspzrFftNip(ReIn,ImIn,ReOut,ImOut,size_power,NSP_Forw);
#else	/* !MATRIX_DOUBLE */
		nspcrFftNip(ReIn,ImIn,ReOut,ImOut,size_power,NSP_Forw);
#endif	/* !MATRIX_DOUBLE */
	}
	else				/* Inverse FFT */
	{
#ifdef	MATRIX_DOUBLE
		nspzrFftNip(ReIn,ImIn,ReOut,ImOut,size_power,NSP_Inv);
#else	/* !MATRIX_DOUBLE */
		nspcrFftNip(ReIn,ImIn,ReOut,ImOut,size_power,NSP_Inv);
#endif	/* !MATRIX_DOUBLE */
	}
}

#elif	defined(HAVE_GSL) /* !HAVE_NSP */

#include <gsl_errno.h>
#include <gsl_fft_complex.h>

void	V_GLOBAL
fftCalcNip(
	mvec_t *ReOut,mvec_t *ImOut,
	mvec_t *ReIn,mvec_t *ImIn,
	unsigned size_power,mvec_t dir_sign)
{
	size_t i,j,len;
	double *packed;
	if(!ReOut || !ImOut || !ReIn || !ImIn || !size_power)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"fftCalcNip(): incorrect parameters.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	len=power2(size_power);
	packed=(double*)memAlloc((len << 1) * sizeof(double));
	for(i=j=0;i<len;i++,j+=2)
	{
		packed[j]=ReIn[i];
		packed[j+1]=ImIn[i];
	}
	if(dir_sign > 0)	/* Forward FFT */
		gsl_fft_complex_radix2_forward(packed,1,len);
	else				/* Inverse FFT */
		gsl_fft_complex_radix2_inverse(packed,1,len);
	for(i=j=0;i<len;i++,j+=2)
	{
		ReOut[i]=packed[j];
		ImOut[i]=packed[j+1];
	}
	memFree(packed);
}

#else	/* !HAVE_GSL */

/*
 * Cut - and - paste from 'Numerical recipies in C'.
 * Copyright (C) RedPec (?) 
 */
#define NRC_FFT_SWAP(a, b) tempr = (a); (a) = (b); (b) = tempr

/*
 * Replaces data by its discrete Fourier transform, if isign is input as 1;
 * or replaces data by nn times its inverse discrete Fourier transform,
 * if isign is input as -1.
 * data is a complex array of length nn, input as a real array data[1..2*nn].
 * nn MUST be an integer power of 2.
*/

V_LOCAL void
nrc_four1(mvec_t data[], int nn, int isign)
{
	int i, n, mmax, m, j, istep;
	mvec_t wtemp, wr, wpr, wpi, wi, theta;
	mvec_t tempr, tempi;
	n = nn << 1;
	j = 1;
	/* This is the bit-reversal section of the routine. */
	for (i = 1; i < n; i += 2)
	{
    	if (j > i)
		{
			/* Exchange the two complex numbers. */
	  		NRC_FFT_SWAP(data[j  ],data[i  ]);
			NRC_FFT_SWAP(data[j+1],data[i+1]);
		};
		m = n >> 1;
		while (m >= 2 && j > m)
		{
			j  -= m;
			m >>= 1;
		};
		j += m;
	};
	/* Here begins the Danielson-Lanczos section of the routine. */
	mmax = 2;
	while (n > mmax)
	{
		/* Initialize trigonometric recurrence. */
		theta	= (mvec_t)6.28318530717959/(isign*mmax);
		wtemp	= (mvec_t)sin(0.5*theta);
		wpr	= (mvec_t)(-2.0)*wtemp*wtemp;
		wpi	= (mvec_t)sin(theta);
		wr	= 1;
		wi	= 0;
		/* Here are the two nested inner loops. */
		istep	= 2*mmax;
		for (m = 1; m < mmax; m += 2)
		{
			for (i = m; i <= n; i += istep)
			{
				/* This is the Danielson-Lanczos formula. */
				j = i + mmax;
				tempr = wr*data[j  ] - wi*data[j+1];
				tempi = wr*data[j+1] + wi*data[j  ];
				data[j  ] = data[i  ] - tempr;
				data[j+1] = data[i+1] - tempi;
				data[i  ] += tempr;
				data[i+1] += tempi;
			}; /* Trigonometric recurrence. */
			wr = (wtemp = wr)*wpr - wi*wpi + wr;
			wi = wi*wpr + wtemp*wpi + wi;
		};
		mmax = istep;
	};
}

/*
 * Wrapper
 */
void	V_GLOBAL
fftCalcNip(
	mvec_t *ReOut,mvec_t *ImOut,
	mvec_t *ReIn,mvec_t *ImIn,
	unsigned size_power,mvec_t dir_sign)
{
	size_t i,len;
	mvec_t *packed;
	if(!ReOut || !ImOut || !ReIn || !ImIn || !size_power)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"fftCalcNip(): incorrect parameters.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	len=power2(size_power);
	packed=(mvec_t*)memAlloc((len << 1) * sizeof(mvec_t));
	for(i=0;i<len;i++)
	{
		packed[i<<1]=ReIn[i];
		packed[(i<<1)+1]=ImIn[i];
	}
	nrc_four1(packed,size_power,dir_sign > 0 ? 1 : -1);
	for(i=0;i<len;i++)
	{
		ReOut[i]=packed[i<<1];
		ImOut[i]=packed[(i<<1)+1];
	}
	memFree(packed);
}

#endif	/* !HAS_NSP */
