#!/bin/sh
# GSL-GENFFT - Power2 FFT C Code Generator
# Copyright (C) 2001 Tim Janik
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

# include files
echo "#include $2"
echo "#include <math.h>"

MKFFT="$1"
IEEE_TYPE="double"
OPTIONS="--double"

# provide macros and inline stubs
$MKFFT $OPTIONS 0

#
# generate small fft sizes, seperating stage 2
#
for dir in --analysis --synthesis ; do
  DOPT="$OPTIONS --skip-macros $dir"
  echo "Generating FFT functions: $dir" >&2
  $MKFFT $DOPT	   2 F				# standalone fft2
  $MKFFT $DOPT	   4 S F			# reusable fft4
  $MKFFT $DOPT	   4 F X			# standalone fft4
  $MKFFT $DOPT	   8 s F F			# reusable fft8 w/o input fft2
  $MKFFT $DOPT	   8 F s X			# standalone fft8
  $MKFFT $DOPT	  16 s F F F			# reusable fft16
  $MKFFT $DOPT	  16 F s s X			# standalone fft16
  $MKFFT $DOPT	  32 s F F F F
  $MKFFT $DOPT	  32 F s s s X
  $MKFFT $DOPT	  64 s R R R R F		# reusable fft64
  $MKFFT $DOPT	  64 F s s s s X		# standalone fft64
  $MKFFT $DOPT	 128 s R R R R R F		# reusable fft128
  $MKFFT $DOPT	 128 L s s s s s X		#
  $MKFFT $DOPT	 256 s s s s s s X T		# reuse fft128
  $MKFFT $DOPT	 256 L s s s s s s X		#
  $MKFFT $DOPT	 512 s s s s s s X T T		# reuse fft128
  $MKFFT $DOPT	 512 L s s s s s s s X		# fft512
  $MKFFT $DOPT	1024 s s s s s s s s X L	#
  $MKFFT $DOPT	1024 L s s s s s s s s X	#
  $MKFFT $DOPT	2048 s s s s s s s s X L L	# reusable fft2048
  $MKFFT $DOPT	2048 L s s s s s s s s s X	#
  $MKFFT $DOPT	4096 s s s s s s s s s s X L	# reuses fft2048
  $MKFFT $DOPT	4096 L s s s s s s s s s s X	# fft4096
  $MKFFT $DOPT	8192 s s s s s s s s s s s X L	# reusable impl. for 8192, reuses 2048
  $MKFFT $DOPT	8192 L s s s s s s s s s s s X	# real impl. for 8192
done

#
# generic variable length implementation
#
echo "Generating generic FFT function for sizes >8192" >&2
cat <<__EOF


/* public FFT frontends and generic handling of huge fft sizes */


static void
gsl_power2_fftc_big (const unsigned int n_values,
		     const $IEEE_TYPE  *rivalues_in,
		     $IEEE_TYPE        *rivalues,
                     const int          esign)
{
  const unsigned int n_values2 = n_values << 1;
  double theta = esign < 0 ? -3.1415926535897932384626433832795029 : 3.1415926535897932384626433832795029;
  unsigned int i, block_size = 8192 << 1;
  double last_sin;

  if (esign > 0)
    {
      if (rivalues_in)
        bitreverse_fft2analysis (n_values, rivalues_in, rivalues);
      for (i = 0; i < n_values; i += 8192)
        gsl_power2_fft8192analysis_skip2 (rivalues + (i << 1), rivalues + (i << 1));
    }
  else
    {
      if (rivalues_in)
        bitreverse_fft2synthesis (n_values, rivalues_in, rivalues);
      for (i = 0; i < n_values; i += 8192)
        gsl_power2_fft8192synthesis_skip2 (rivalues + (i << 1), rivalues + (i << 1));
    }
  theta *= (double) 1.0 / 8192.;
  last_sin = sin (theta);
  
  /* we're not combining the first and second halves coefficients
   * in the below loop, since for fft sizes beyond 8192, it'd actually
   * harm performance due to paging
   */
  do
    {
      double Dre, Dim, Wre, Wim;
      unsigned int k, i, half_block = block_size >> 1;
      unsigned int block_size2 = block_size << 1;

      theta *= 0.5;
      Dim = last_sin;
      last_sin = sin (theta);
      Dre = last_sin * last_sin * -2.;
      
      /* loop over first coefficient in each block ==> w == {1,0} */
      for (i = 0; i < n_values2; i += block_size2)
	{
	  unsigned int v1 = i, v2 = i + block_size;

          BUTTERFLY_10 (rivalues[v1], rivalues[v1 + 1],
                        rivalues[v2], rivalues[v2 + 1],
                        rivalues[v1], rivalues[v1 + 1],
                        rivalues[v2], rivalues[v2 + 1],
                        __1, __0);
	}
      Wre = Dre + 1.0;	/* update Wk */
      Wim = Dim;	/* update Wk */
      /* loop for every Wk in the first half of each subblock */
      for (k = 2; k < half_block; k += 2)
	{
	  /* loop over kth coefficient in each block */
	  for (i = k; i < n_values2; i += block_size2)
	    {
	      unsigned int v1 = i, v2 = i + block_size;
	      
              BUTTERFLY_XY (rivalues[v1], rivalues[v1 + 1],
                            rivalues[v2], rivalues[v2 + 1],
                            rivalues[v1], rivalues[v1 + 1],
                            rivalues[v2], rivalues[v2 + 1],
                            Wre, Wim);
	    }
	  WMULTIPLY (Wre, Wim, Dre, Dim);	/* update Wk */
	}
      /* handle middle coefficient ==> w == {0,+-1} */
      if (k < block_size)
	{
	  /* loop over kth coefficient in each block */
	  if (esign > 0)
	    for (i = k; i < n_values2; i += block_size2)
	      {
	        unsigned int v1 = i, v2 = i + block_size;
	      
                BUTTERFLY_01 (rivalues[v1], rivalues[v1 + 1],
                              rivalues[v2], rivalues[v2 + 1],
                              rivalues[v1], rivalues[v1 + 1],
                              rivalues[v2], rivalues[v2 + 1],
                              __0, __1);
	      }
	  else
	    for (i = k; i < n_values2; i += block_size2)
	      {
	        unsigned int v1 = i, v2 = i + block_size;
	      
                BUTTERFLY_0m (rivalues[v1], rivalues[v1 + 1],
                              rivalues[v2], rivalues[v2 + 1],
                              rivalues[v1], rivalues[v1 + 1],
                              rivalues[v2], rivalues[v2 + 1],
                              __0, __1);
	      }
	  /* update Wk */
	  if (esign > 0)
	    {
	      Wre = -Dim;
	      Wim = Dre + 1.0;
	    }
	  else
	    {
	      Wre = Dim;
	      Wim = -Dre - 1.0;
	    }
	  k += 2;
	}
      /* loop for every Wk in the second half of each subblock */
      for (; k < block_size; k += 2)
	{
	  /* loop over kth coefficient in each block */
	  for (i = k; i < n_values2; i += block_size2)
	    {
	      unsigned int v1 = i, v2 = i + block_size;

              BUTTERFLY_XY (rivalues[v1], rivalues[v1 + 1],
                            rivalues[v2], rivalues[v2 + 1],
                            rivalues[v1], rivalues[v1 + 1],
                            rivalues[v2], rivalues[v2 + 1],
                            Wre, Wim);
	    }
	  WMULTIPLY (Wre, Wim, Dre, Dim);	/* update Wk */
	}
      block_size = block_size2;
    }
  while (block_size <= n_values);
}
__EOF


#
# public complex fft frontends
#
echo "Generating public complex FFT frontends" >&2
cat <<__EOF
void
gsl_power2_fftac (const unsigned int n_values,
                  const $IEEE_TYPE  *rivalues_in,
                  $IEEE_TYPE        *rivalues_out)
{
  g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 1);
  
  switch (n_values)
    {
      case    1: rivalues_out[0] = rivalues_in[0], rivalues_out[1] = rivalues_in[1]; break;
      case    2: gsl_power2_fft2analysis (rivalues_in, rivalues_out);		break;
      case    4: gsl_power2_fft4analysis (rivalues_in, rivalues_out);		break;
      case    8: gsl_power2_fft8analysis (rivalues_in, rivalues_out);		break;
      case   16: gsl_power2_fft16analysis (rivalues_in, rivalues_out);		break;
      case   32: gsl_power2_fft32analysis (rivalues_in, rivalues_out);		break;
      case   64: gsl_power2_fft64analysis (rivalues_in, rivalues_out);		break;
      case  128: gsl_power2_fft128analysis (rivalues_in, rivalues_out);		break;
      case  256: gsl_power2_fft256analysis (rivalues_in, rivalues_out);		break;
      case  512: gsl_power2_fft512analysis (rivalues_in, rivalues_out);		break;
      case 1024: gsl_power2_fft1024analysis (rivalues_in, rivalues_out);	break;
      case 2048: gsl_power2_fft2048analysis (rivalues_in, rivalues_out);	break;
      case 4096: gsl_power2_fft4096analysis (rivalues_in, rivalues_out);	break;
      case 8192: gsl_power2_fft8192analysis (rivalues_in, rivalues_out);	break;
      default:	 gsl_power2_fftc_big (n_values, rivalues_in, rivalues_out, +1);
    }
}
void
gsl_power2_fftsc (const unsigned int n_values,
                  const $IEEE_TYPE  *rivalues_in,
                  $IEEE_TYPE        *rivalues_out)
{
  g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 1);
  
  switch (n_values)
    {
      case    1: rivalues_out[0] = rivalues_in[0], rivalues_out[1] = rivalues_in[1]; break;
      case    2: gsl_power2_fft2synthesis (rivalues_in, rivalues_out);		break;
      case    4: gsl_power2_fft4synthesis (rivalues_in, rivalues_out);		break;
      case    8: gsl_power2_fft8synthesis (rivalues_in, rivalues_out);		break;
      case   16: gsl_power2_fft16synthesis (rivalues_in, rivalues_out);		break;
      case   32: gsl_power2_fft32synthesis (rivalues_in, rivalues_out);		break;
      case   64: gsl_power2_fft64synthesis (rivalues_in, rivalues_out);		break;
      case  128: gsl_power2_fft128synthesis (rivalues_in, rivalues_out);	break;
      case  256: gsl_power2_fft256synthesis (rivalues_in, rivalues_out);	break;
      case  512: gsl_power2_fft512synthesis (rivalues_in, rivalues_out);	break;
      case 1024: gsl_power2_fft1024synthesis (rivalues_in, rivalues_out);	break;
      case 2048: gsl_power2_fft2048synthesis (rivalues_in, rivalues_out);	break;
      case 4096: gsl_power2_fft4096synthesis (rivalues_in, rivalues_out);	break;
      case 8192: gsl_power2_fft8192synthesis (rivalues_in, rivalues_out);	break;
      default:	 gsl_power2_fftc_big (n_values, rivalues_in, rivalues_out, -1);
    }
  /* { unsigned int i; for (i = 0; i < n_values << 1; i++) rivalues_out[i] *= (double) n_values; } */
}
__EOF


#
# public real fft frontends
#
echo "Generating public real FFT frontends" >&2
cat <<__EOF
void
gsl_power2_fftar (const unsigned int n_values,
                  const $IEEE_TYPE  *r_values_in,
                  $IEEE_TYPE        *rivalues_out)
{
  unsigned int n_cvalues = n_values >> 1;
  double Dre, Dim, Wre, Wim, theta;
  unsigned int i;

  g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 2);

  gsl_power2_fftac (n_cvalues, r_values_in, rivalues_out);
  theta = 3.1415926535897932384626433832795029;
  theta /= (double) n_cvalues;

  Dre = sin (0.5 * theta);
  Dim = sin (theta);
  Dre = Dre * Dre;
  Wre = 0.5 - Dre;
  Dre *= -2.;
  Wim = Dim * 0.5;
  for (i = 2; i < n_values >> 1; i += 2)
    {
      double F1re, F1im, F2re, F2im, H1re, H1im, H2re, H2im;
      unsigned int r = n_values - i;
      double FEre = rivalues_out[i] + rivalues_out[r];
      double FEim = rivalues_out[i + 1] - rivalues_out[r + 1];
      double FOre = rivalues_out[r] - rivalues_out[i];
      double FOim = rivalues_out[r + 1] + rivalues_out[i + 1];

      FEre *= 0.5;
      FEim *= 0.5;
      F2re = FOre * Wim;
      F2im = FOim * Wim;
      F1re = FOre * Wre;
      F1im = FOim * Wre;

      H1im = F2im + F1re;
      H1re = F1im - F2re;
      H2re = F2re - F1im;
      H2im = H1im - FEim;
      H1re += FEre;
      H2re += FEre;
      H1im += FEim;
      rivalues_out[i] = H1re;
      rivalues_out[i + 1] = H1im;
      rivalues_out[r] = H2re;
      rivalues_out[r + 1] = H2im;
      WMULTIPLY (Wre, Wim, Dre, Dim);
    }
  Dre = rivalues_out[0];
  rivalues_out[0] = Dre + rivalues_out[1];
  rivalues_out[1] = Dre - rivalues_out[1];
}
void
gsl_power2_fftsr (const unsigned int n_values,
                  const double      *rivalues_in,
                  double            *r_values_out)
{
  unsigned int n_cvalues = n_values >> 1;
  double Dre, Dim, Wre, Wim, theta, scale;
  unsigned int i, ri;

  g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 2);

  theta = -3.1415926535897932384626433832795029;
  theta /= (double) n_cvalues;

  Dre = sin (0.5 * theta);
  Dim = sin (theta);
  Dre = Dre * Dre;
  Wre = 0.5 - Dre;
  Dre *= -2.;
  Wim = Dim * 0.5;
  for (i = 2, ri = 0; i < n_values >> 1; i += 2)
    {
      double F1re, F1im, F2re, F2im, H1re, H1im, H2re, H2im;
      unsigned int g = n_values - i, j = n_values >> 2, rg = n_values - (ri << 1) - 2;
      double FEre = rivalues_in[i] + rivalues_in[g];
      double FEim = rivalues_in[i + 1] - rivalues_in[g + 1];
      double FOre = rivalues_in[g] - rivalues_in[i];
      double FOim = rivalues_in[g + 1] + rivalues_in[i + 1];

      while (ri >= j)
        {
          ri -= j;
          j >>= 1;
        }
      ri |= j;

      FOre = -FOre;
      FOim = -FOim;
      FEre *= 0.5;
      FEim *= 0.5;
      F2re = FOre * Wim;
      F2im = FOim * Wim;
      F1re = FOre * Wre;
      F1im = FOim * Wre;

      H1im = F2im + F1re;
      H1re = F1im - F2re;
      H2re = F2re - F1im;
      H2im = H1im - FEim;
      H1re += FEre;
      H2re += FEre;
      H1im += FEim;

      j = ri << 1;
      r_values_out[j] = H1re;
      r_values_out[j + 1] = H1im;
      r_values_out[rg] = H2re;
      r_values_out[rg + 1] = H2im;
      WMULTIPLY (Wre, Wim, Dre, Dim);
    }
  Dre = rivalues_in[0];
  r_values_out[0] = Dre + rivalues_in[1];
  r_values_out[1] = Dre - rivalues_in[1];
  r_values_out[0] *= 0.5;
  r_values_out[1] *= 0.5;
  if (n_values < 4)
    return;
  r_values_out[2] = rivalues_in[i];
  r_values_out[2 + 1] = rivalues_in[i + 1];
  scale = n_cvalues;
  scale = 1.0 / scale;
  for (i = 0; i < n_values; i += 4)
    BUTTERFLY_10scale (r_values_out[i], r_values_out[i + 1],
                       r_values_out[i + 2], r_values_out[i + 3],
                       r_values_out[i], r_values_out[i + 1],
                       r_values_out[i + 2], r_values_out[i + 3],
                       scale);
  switch (n_cvalues)
    {
      case    2: break;
      case    4: gsl_power2_fft4synthesis_skip2 (NULL, r_values_out);	 break;
      case    8: gsl_power2_fft8synthesis_skip2 (NULL, r_values_out);	 break;
      case   16: gsl_power2_fft16synthesis_skip2 (NULL, r_values_out);	 break;
      case   32: gsl_power2_fft32synthesis_skip2 (NULL, r_values_out);	 break;
      case   64: gsl_power2_fft64synthesis_skip2 (NULL, r_values_out);	 break;
      case  128: gsl_power2_fft128synthesis_skip2 (NULL, r_values_out);	 break;
      case  256: gsl_power2_fft256synthesis_skip2 (NULL, r_values_out);	 break;
      case  512: gsl_power2_fft512synthesis_skip2 (NULL, r_values_out);	 break;
      case 1024: gsl_power2_fft1024synthesis_skip2 (NULL, r_values_out); break;
      case 2048: gsl_power2_fft2048synthesis_skip2 (NULL, r_values_out); break;
      case 4096: gsl_power2_fft4096synthesis_skip2 (NULL, r_values_out); break;
      case 8192: gsl_power2_fft8192synthesis_skip2 (NULL, r_values_out); break;
      default:	 gsl_power2_fftc_big (n_cvalues, NULL, r_values_out, -1);
    }
}
void
gsl_power2_fftar_simple (const unsigned int n_values,
                         const float       *real_values,
                         float             *complex_values)
{
  double *rv, *cv;
  guint i;

  g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 2);

  rv = g_new (double, n_values * 2);
  cv = rv + n_values;
  i = n_values;
  while (i--)
    rv[i] = real_values[i];
  gsl_power2_fftar (n_values, rv, cv);
  i = n_values;
  while (i--)
    complex_values[i] = cv[i];
  complex_values[n_values] = complex_values[1];
  complex_values[1] = 0.0;
  complex_values[n_values + 1] = 0.0;
  g_free (rv);
}
void
gsl_power2_fftsr_simple (const unsigned int n_values,
                         const float       *complex_values,
                         float             *real_values)
{
  double *cv, *rv;
  guint i;

  g_return_if_fail ((n_values & (n_values - 1)) == 0 && n_values >= 2);

  cv = g_new (double, n_values * 2);
  rv = cv + n_values;
  i = n_values;
  while (i--)
    cv[i] = complex_values[i];
  cv[1] = complex_values[n_values];
  gsl_power2_fftsr (n_values, cv, rv);
  i = n_values;
  while (i--)
    real_values[i] = rv[i];
  g_free (cv);
}
__EOF
