/***************************************************************************
**
**  This file is part of GeopsyCore.
**
**  GeopsyCore 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 3 of the License, or
**  (at your option) any later version.
**
**  GeopsyCore 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 Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2009-04-11
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>
#include "FastPartialFourierTransform.h"
#include "DoubleSignal.h"
#include "ComplexSignal.h"
#include "FastFourierTransform.h"

namespace GeopsyCore {

/*!
  \class FastPartialFourierTransform FastPartialFourierTransform.h
  \brief Brief description of class still missing

   Partial fast Fourier transform, transform a sequence of n samples
   into a sequence of m samples after a Fourier transform.
   m*q=n, where q is an integer.

   The algorithm is based on some comments in Bailey and Swarztrauber 1995
   "The fractional Fourier Transform and Applications, SIAM Review,
   vol. 33 no. 3 (Sept. 1991, pg. 389-404)"

   F(k+s)[x]=sum j from 0 to q-1 {exp(-2*pi*i*j*(k+s)/n)*F(k)[z(j)] }, o<=k<m

   Where z(j)=x(j+l*q)*exp(-2*pi*i*l*s/m), 0<=l<m

   The current type() determine the direction of the transform. If it is currently
   in time domain: spectrum is computed from \a min Hz to \a max Hz. On the contrary,
   if it is in frequency domain: the waveform is computed from min s. to max .s rather
   than from 0 to 1/df with the classical fastFourierTransform().

   If the total input number samples is not a multiple of the output range (in terms
   of number of samples), some samples at the end of signal can be ignored. If the
   observed q is less than 10, a normal fast Fourier tranform is started.

   This class is obsolete, because no significant time improvement can be achieved compared
   to a complete FFT transform.
*/

#if 0
IMPLEMENT_NUMERICALKEY(FastPartialFourierTransformKey)

/*!
  Description of destructor still missing
*/
FastPartialFourierTransform::FastPartialFourierTransform(FastPartialFourierTransformKey * key)
  : AbstractNumericalCache(key)
{
  _inIndexes=0;
  _outIndexes=0;
  _zFactors=0;
  _fzFactors=0;
}

/*!
  Description of destructor still missing
*/
FastPartialFourierTransform::~FastPartialFourierTransform()
{
  delete [] _inIndexes;
  delete [] _outIndexes;
  delete [] _zFactors;
  delete [] _fzFactors;
}

/*!
*/
void FastPartialFourierTransform::init()
{
  const FastPartialFourierTransformKey& fKey=static_cast<const FastPartialFourierTransformKey&>(key());
  long int q=fKey.nInSamples()/fKey.mOutSamples();
  ASSERT(q*fKey.mOutSamples()==fKey.nInSamples());

  _inIndexes=new int[fKey.nInSamples()];
  _outIndexes=new int[fKey.nInSamples()];
  _zFactors=new Complex[fKey.nInSamples()];
  _fzFactors=new Complex[fKey.nInSamples()];

  ComplexExponential::Sign sign;
  SAFE_UNINITIALIZED(sign, ComplexExponential::Negative);
  double normFactor;
  SAFE_UNINITIALIZED(normFactor, 0);
  switch(fKey.direction()) {
  case FastPartialFourierTransformKey::Forward:
    normFactor=1.0/(fKey.deltaT()*fKey.nInSamples());
    sign=ComplexExponential::Negative;
    break;
  case FastPartialFourierTransformKey::Backward:
    normFactor=fKey.deltaT();
    sign=ComplexExponential::Positive;
    break;
  }
  ComplexExponential expn(fKey.nInSamples(), sign);
  ComplexExponential expm(fKey.mOutSamples(), sign);

  int index;
  // Computation of zFactors may look a bit stupid, but exp values are cached
  // so it does not cost so much to compute them q times and will be faster
  // for the final computation.
  index=0;
  for(long int j=0; j < q; j++ ) {
    for(long int l=0; l < fKey.mOutSamples(); l++ ) {
      _zFactors[index]=expm.value(l*fKey.startIndex());
      _inIndexes[index]=j+l*q;
      index++;
    }
  }
  index=0;
  for(long int j=0; j < q; j++ ) {
    for(long int k=0; k < fKey.mOutSamples(); k++ ) {
      Complex& c=_fzFactors[index];
      c=expn.value((k+fKey.startIndex())*j);
      c *= normFactor;
      _outIndexes[index]=k;
      index++;
    }
  }
}

ComplexSignal * FastPartialFourierTransform::forward(const DoubleSignal * inSig, int nInSamples, int mOutSamples, int startIndex)
{
  const FastPartialFourierTransform * cache=begin(inSig->deltaT(), nInSamples, mOutSamples, startIndex,
                                                        FastPartialFourierTransformKey::Forward);
  ComplexSignal * outSig=0;
  CONST_LOCK_SAMPLES(double, inSamples, inSig)
    int q=nInSamples/mOutSamples;
    ComplexSignal z(nInSamples);
    LOCK_SAMPLES(Complex, zSamples, (&z))
      // zj
      for(int i=0;i<nInSamples; i++) {
        Complex& c=zSamples[i];
        c=cache->zFactor(i);
        c*=inSamples[cache->inIndex(i)];
      }
      // Fk(zj)
      FastFourierTransform::instance()->manyForward(q, mOutSamples, zSamples);
      outSig=new ComplexSignal(nInSamples);
      LOCK_SAMPLES(Complex, outSamples, outSig)
        for(int i=0;i<nInSamples; i++) {
          Complex& c=zSamples[i];
          c*=cache->fzFactor(i);
          outSamples[cache->outIndex(i)]+=c;
        }
      UNLOCK_SAMPLES(outSig)
    UNLOCK_SAMPLES((&z));
  UNLOCK_SAMPLES(inSig);
  end(cache);
  return outSig;
}

ComplexSignal * FastPartialFourierTransform::backward(const DoubleSignal * inSig, int nInSamples, int mOutSamples, int startIndex)
{
  const FastPartialFourierTransform * cache=begin(inSig->deltaT(), nInSamples, mOutSamples, startIndex,
                                                        FastPartialFourierTransformKey::Backward);
  ComplexSignal * outSig=0;
  CONST_LOCK_SAMPLES(double, inSamples, inSig)
    int q=nInSamples/mOutSamples;
    ComplexSignal z(nInSamples);
    LOCK_SAMPLES(Complex, zSamples, (&z))
      // zj
      for(int i=0;i<nInSamples; i++) {
        Complex& c=zSamples[i];
        c=cache->zFactor(i);
        c*=inSig->complex(inSamples, cache->inIndex(i));
      }
      // Fk(zj)
      FastFourierTransform::instance()->manyBackward(q, mOutSamples, zSamples);
      outSig=new ComplexSignal(mOutSamples);
      LOCK_SAMPLES(Complex, outSamples, outSig)
        for(int i=0;i<nInSamples; i++) {
          Complex& c=zSamples[i];
          c*=cache->fzFactor(i);
          outSamples[cache->outIndex(i)]+=c;
        }
      UNLOCK_SAMPLES(outSig)
    UNLOCK_SAMPLES((&z));
    end(cache);
  UNLOCK_SAMPLES(inSig);
  return outSig;
}
#endif

} // namespace GeopsyCore
