/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 European Southern Observatory
 *
 * 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */
/*---------------------------------------------------------------------------

   File name     :    sinfo_fft_base.c
   Author         :    N. Devillard
   Created on    :    October 1999
   Description    :    base FFT routines

 *--------------------------------------------------------------------------*/
/*
    $Id: sinfo_fft_base.c,v 1.7 2012-03-02 08:42:20 amodigli Exp $
    $Author: amodigli $
    $Date: 2012-03-02 08:42:20 $
    $Revision: 1.7 $
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
/*---------------------------------------------------------------------------
                                   Includes
 ---------------------------------------------------------------------------*/
#include "sinfo_fft_base.h"
#include "sinfo_globals.h"
/**@{*/
/**
 * @addtogroup sinfo_utilities Function to compute FFT
 *
 * TBD
 */
/*---------------------------------------------------------------------------
                              Function codes
 ---------------------------------------------------------------------------*/
/**
  @name        sinfo_fftn
  @memo        N-dimensional FFT.
  @param    data        N-dimensional data set stored in 1d.
  @param    nn            Dimensions of the set.
  @param    ndim        How many dimensions this set has.
  @param    isign        Transform direction.
  @return    void
  @doc

  This routine is a public domain FFT. See extract of Usenet article
  below. Found on {\tt  http://www.tu-chemnitz.de/~arndt/joerg.html}.

  \begin{verbatim}
  From: alee@tybalt.caltech.edu (Andrew Lee)
  Newsgroups: comp.sources.misc
  Subject: N-dimensional, Radix 2 FFT Routine
  Date: 17 Jul 87 22:26:29 GMT
  Approved: allbery@ncoast.UUCP
  X-Archive: comp.sources.misc/8707/48

  [..]
  Now for the usage (finally):
  data[] is the array of complex numbers to be transformed,
  nn[] is the array giving the dimensions (I mean size) of the array,
  ndim is the number of dimensions of the array, and
  isign is +1 for a forward transform, and -1 for an inverse transform.

  data[] and nn[] are stored in the "natural" order for C:
  nn[0] gives the number of elements along the leftmost index,
  nn[ndim - 1] gives the number of elements along the rightmost index, and
  data should be declared along the lines of
  struct (f)complex data[nn[0], nn[1], ..., nn[ndim - 1]]

  Additional notes: The routine does NO NORMALIZATION, so if you do a
  forward, and then an inverse transform on an array, the result will
  be identical to the original array MULTIPLIED BY THE NUMBER OF
  ELEMENTS IN THE ARRAY.  Also, of course, the dimensions of data[]
  must all be powers of 2.
  \end{verbatim}

 */

void
sinfo_fftn(
                dcomplex data[],
                unsigned nn[],
                int ndim,
                int isign)
{
    int        idim=0;
    unsigned   i1=0;
    unsigned   i2rev=0;
    unsigned   i3rev=0;
    unsigned   ibit=0;
    unsigned   ip2=0;
    unsigned   ifp1=0;
    unsigned   ifp2=0;
    unsigned   k2=0;
    unsigned   n=0;
    unsigned   nprev = 1;
    unsigned   ntot = 1;
    register   unsigned i2=0;
    register   unsigned i3=0;
    double        theta=0;
    dcomplex   w, wp;
    double        wtemp=0;
    dcomplex   temp, wt;
    double       t1=0;
    double     t2=0;

    /*      Compute total number of complex values  */
    for (idim = 0; idim < ndim; ++idim) {
        ntot *= nn[idim];
    }

    for (idim = ndim - 1; idim >= 0; --idim) {
        n = nn[idim];

        ip2 = nprev * n;        /*  Unit step for next dimension */
        i2rev = 0;              /*  Bit reversed i2 */

        /*      This is the bit reversal section of the routine */
        /*      Loop over current dimension     */
        for (i2 = 0; i2 < ip2; i2 += nprev) {
            if (i2 < i2rev) {
                /*      Loop over lower dimensions      */
                for (i1 = i2; i1 < i2 + nprev; ++i1) {
                    /*      Loop over higher dimensions  */
                    for (i3 = i1; i3 < ntot; i3 += ip2) {
                        i3rev = i3 + i2rev - i2;
                        temp = data[i3];
                        data[i3] = data[i3rev];
                        data[i3rev] = temp;
                    }
                }
            }
            ibit = ip2;
            /*      Increment from high end of i2rev to low */
            do {
                ibit >>= 1;
                i2rev ^= ibit;
            } while (ibit >= nprev && !(ibit & i2rev));
        }

        /*      Here begins the Danielson-Lanczos section of the routine */
        /*      Loop over step sizes    */
        for (ifp1 = nprev; ifp1 < ip2; ifp1 <<= 1) {
            ifp2 = ifp1 << 1;
            /*  Initialize for the trig. recurrence */
            theta = isign * 2.0 * PI_NUMB / (ifp2 / nprev);
            wp.x = sin(0.5 * theta);
            wp.x *= -2.0 * wp.x;
            wp.y = sin(theta);
            w.x = 1.0;
            w.y = 0.0;

            /*  Loop by unit step in current dimension  */
            for (i3 = 0; i3 < ifp1; i3 += nprev) {
                /*      Loop over lower dimensions      */
                for (i1 = i3; i1 < i3 + nprev; ++i1) {
                    /*  Loop over higher dimensions */
                    for (i2 = i1; i2 < ntot; i2 += ifp2) {
                        /*      Danielson-Lanczos formula */
                        k2 = i2 + ifp1;
                        wt = data[k2];

                        /* Complex multiply using 3 real multiplies.
               Should usually be faster.    */
                        data[k2].x = data[i2].x - (temp.x =
                                        (t1 = w.x * wt.x) - (t2 = w.y * wt.y));
                        data[k2].y = data[i2].y - (temp.y =
                                        (w.x + w.y) * (wt.x + wt.y) - t1 - t2);
                        data[i2].x += temp.x;
                        data[i2].y += temp.y;
                    }
                }
                /*      Trigonometric recurrence        */
                wtemp = w.x;
                /*    Complex multiply using 3 real multiplies.    */
                w.x += (t1 = w.x * wp.x) - (t2 = w.y * wp.y);
                w.y += (wtemp + w.y) * (wp.x + wp.y) - t1 - t2;
            }
        }
        nprev *= n;
    }

    return ;
}




/**
  @name        sinfo_is_power_of_2
  @memo        Find if a given integer is a power of 2.
  @param    p    Integer to check.
  @return    The corresponding power of 2, or -1.
  @doc

  If the given number is a power of 2, the power is returned.
  Otherwise -1 is returned.

  Example:
  \begin{verbatim}
  sinfo_is_power_of_2(1024) returns 10
  sinfo_is_power_of_2(1023) returns -1
  \end{verbatim}
 */

int
sinfo_is_power_of_2(int p)
{
    float    c ;
    int        power2 ;

    if (p == 0) { /* Yes, 0 is a power of 2    */
        power2 =  1 ;
    } else if (p<0) { /* No, negatives are no power of 2 (in R at least) */
        power2 = -1 ;
    } else { /* Compute log in base 2    */
        c = (float)(log((double)p) / log(2.0)) ;
        if (c == (float)((int)c)) {
            power2 = (int)c ;
        } else {
            power2 = -1 ;
        }
    }

    return power2 ;
}

/**@}*/
