/***************************************************************************
  **************************************************************************
  
  SOFT: SO(3) Fourier Transforms
  Version 2.0

  Copyright (c) 2003, 2004, 2007 Peter Kostelec, Dan Rockmore
  
  This file is part of SOFT.

  SOFT 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.

  SOFT 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, see <http://www.gnu.org/licenses/>.
  
  See the accompanying LICENSE file for details.
  
  ************************************************************************
  ************************************************************************/

/*

 a somewhat memory-friendly test routine to rotate a spherical function
 by massaging its S^2 Fourier coefficients with Wigner-D functions

 bwIn = bandwidth of input signal
 bwOut = bandwidth of output signal (can up- or down-sample)
 degOut = max degree of spherical harmonic you want to use ( < bwOut )
 alpha, beta, gamma -> the three Euler angles

             0 <= alpha, gamma < 2*pi
             0 <= beta <= pi

 inputSamples -> filename of input samples
 outputSamples -> filename of output (rotated) samples

 isReal = 1: samples are strictly real (so no imaginary parts)
 isReal = 0: samples are complex (so in interleaved format)


 Here are order of rotation events:
  1) rotate by gamma about the z-axis
  2) rotate by beta about the y-axis
  3) rotate by alpha about the z-axis.

 example: test_s2_rotate_fftw bwIn bwOut degOut alpha beta gamma inputSamples outputSamples isReal

 example: test_s2_rotate_fftw 32 16 15 0.37 2.32 4.37 fctIn.dat fctOut.dat 0



 NOTE: Sometimes there is a segmentation fault *after* all the rotating and
 writing out of the output file is complete. I haven't tracked this down yet,
 but I believe it has to do with freeing up the memory associated with doing
 the S^2 transforms ... my array of double pointers are not pointing in the
 right places when I try to free memory. However, the rotation itself is
 correct.

*/

#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "csecond.h"
#include "s2_cospmls.h"
#include "rotate_so3_fftw.h"

/***********************

 s2RotateFFTW: wrapper function version of

           test_s2_rotate_fftw.c

 will rotate a possibly complex-valued function defined on the sphere by
 the Euler angles alpha, beta, gamma, where

	           0 <= alpha, gamma < 2*pi
	           0 <= beta <= pi

 and in the following order

                   1) rotate by gamma about the z-axis
                   2) rotate by beta about the y-axis
                   3) rotate by alpha about the z-axis.
		   
 bw: bandwidth of function

 sigIn : ptr to the input signal; if complex, then it's an interleaved
        array of length 2*(2*bw)^2; if strictly real, then it's an
        array of length (2*bw)^2

 sigOut : ptr to the rotated signal; if complex, then it's an interleaved
          array of length 2*(2*bw)^2; if strictly real, then it's an
          array of length (2*bw)^2

 alpha, beta, gamma: doubles specifying the Euler angles.

 isReal: = 1 data is strictly real
         = 0 data is complex (interleaved format)

 NOTE: Sometimes there is a segmentation fault *after* all the rotating and
 writing out of the output file is complete. I haven't tracked this down yet,
 but I believe it has to do with freeing up the memory associated with doing
 the S^2 transforms ... my array of double pointers are not pointing in the
 right places when I try to free memory. However, the rotation itself is
 correct.

****************/

void s2RotateFFTW( int bw,
		   double *sigIn,
		   double *sigOut,
		   double alpha, double beta, double gamma,
		   int isReal)
{
  int i, degOut ;
  double *scratch ;
  double *tmpInR, *tmpInI, *tmpOutR, *tmpOutI ;
  double *seminaive_naive_tablespace ;
  double *trans_seminaive_naive_tablespace;
  double **seminaive_naive_table ;
  double **trans_seminaive_naive_table;

  degOut = bw - 1 ;


  tmpInR = (double *) malloc(sizeof(double)*(4*bw*bw));
  tmpInI = (double *) malloc(sizeof(double)*(4*bw*bw));
  tmpOutR = (double *) malloc(sizeof(double)*(4*bw*bw));
  tmpOutI = (double *) malloc(sizeof(double)*(4*bw*bw));


  scratch = (double *) malloc(sizeof(double)*((14*bw*bw) + (52*bw) + (2*bw)));
    
  seminaive_naive_tablespace =
    (double *) malloc(sizeof(double) *
		      (Reduced_Naive_TableSize(bw,bw) +
		       Reduced_SpharmonicTableSize(bw,bw)));

  trans_seminaive_naive_tablespace =
    (double *) malloc(sizeof(double) *
		      (Reduced_Naive_TableSize(bw,bw) +
		       Reduced_SpharmonicTableSize(bw,bw)));


  /****
       At this point, check to see if all the memory has been
       allocated. If it has not, there's no point in going further.
  ****/

  if ( (scratch == NULL) || 
       (tmpInR == NULL) || (tmpInI == NULL) ||
       (tmpOutR == NULL) || (tmpOutI == NULL) ||
       (seminaive_naive_tablespace == NULL) ||
       (trans_seminaive_naive_tablespace == NULL) )
    {
      perror("Error in allocating memory");
      exit( 1 ) ;
    }
  

  /* precompute for the S^2 transform */
  seminaive_naive_table = SemiNaive_Naive_Pml_Table(bw, bw,
						    seminaive_naive_tablespace,
						    scratch);

  trans_seminaive_naive_table =
    Transpose_SemiNaive_Naive_Pml_Table(seminaive_naive_table,
					bw, bw,
					trans_seminaive_naive_tablespace,
					scratch);

  /* load in signal */
  if ( isReal )
    {
      for( i = 0 ; i < 4*bw*bw ; i ++ )
	{
	  tmpInR[i] = sigIn[i];
	  tmpInI[i] = 0. ;
	}
    }
  else
    {
      for( i = 0 ; i < 4*bw*bw ; i ++ )
	{
	  tmpInR[i] = sigIn[2*i];
	  tmpInI[i] = sigIn[2*i+1];
	}
    }

  /* now rotate */
  rotateFctFFTW( bw, bw, degOut,
		 tmpInR, tmpInI,
		 tmpOutR, tmpOutI,
		 alpha, beta, gamma,
		 scratch,
		 seminaive_naive_table,
		 trans_seminaive_naive_table ) ;

  if ( isReal )
    {
      for( i = 0 ; i < 4*bw*bw ; i ++ )
	{
	  sigOut[i] = tmpOutR[i];
	}
    }
  else
    {
      for( i = 0 ; i < 4*bw*bw ; i ++ )
	{
	  sigOut[2*i] = tmpOutR[i];
	  sigOut[2*i+1] = tmpOutI[i];
	}
    }




  /* clean up */
  free(trans_seminaive_naive_table);
  free(seminaive_naive_table);
  free(trans_seminaive_naive_tablespace);
  free(seminaive_naive_tablespace); 
  free(scratch);
  free(tmpOutI);
  free(tmpOutR);
  free(tmpInI);
  free(tmpInR);


}


