#line 1 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
/* -*- c -*-
|| This file is part of Pike. For copyright information see COPYRIGHT.
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING
|| for more information.
|| $Id: transforms.cmod,v 1.13 2004/03/21 06:09:46 nilsson Exp $
*/

#include "global.h"

#include "interpret.h"
#include "svalue.h"
#include "array.h"
#include "pike_macros.h"
#include "program.h"
#include "stralloc.h"
#include "object.h"
#include "pike_types.h"
#include "threads.h"
#include "dynamic_buffer.h"
#include "builtin_functions.h"
#include "config.h"

#if defined(HAVE_FFTW_H) && defined(HAVE_LIBFFTW) && defined(HAVE_RFFTW_H) && defined(HAVE_LIBRFFTW)
#define HAVE_RFFTW
#endif

#ifdef HAVE_RFFTW

#ifdef HAVE_FFTW_H
#include <fftw.h>
#endif

#ifdef HAVE_RFFTW_H
#include <rfftw.h>
#endif

/*! @module Math
 */

/*! @module Transforms
 */

/*! @class FFT
 */


#undef class_FFT_defined
#define class_FFT_defined
struct program *FFT_program=0;
int FFT_program_fun_num=-1;

#undef var_r_in_FFT_defined
#define var_r_in_FFT_defined

#undef var_r_out_FFT_defined
#define var_r_out_FFT_defined

#undef var_r_cr_plan_FFT_defined
#define var_r_cr_plan_FFT_defined

#undef var_r_rc_plan_FFT_defined
#define var_r_rc_plan_FFT_defined

#undef var_r_cr_plan_size_FFT_defined
#define var_r_cr_plan_size_FFT_defined

#undef var_r_rc_plan_size_FFT_defined
#define var_r_rc_plan_size_FFT_defined

#undef THIS
#define THIS ((struct FFT_struct *)(Pike_interpreter.frame_pointer->current_storage))

#undef THIS_FFT
#define THIS_FFT ((struct FFT_struct *)(Pike_interpreter.frame_pointer->current_storage))

#undef OBJ2_FFT
#define OBJ2_FFT(o) ((struct FFT_struct *)(o->storage+FFT_storage_offset))

#undef GET_FFT_STORAGE
#define GET_FFT_STORAGE ((struct FFT_struct *)(o->storage+FFT_storage_offset)
static ptrdiff_t FFT_storage_offset;
struct FFT_struct {

#ifdef var_r_in_FFT_defined
#line 48 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
fftw_real *r_in;
#endif /* var_r_in_FFT_defined */

#ifdef var_r_out_FFT_defined
#line 49 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
fftw_real *r_out;
#endif /* var_r_out_FFT_defined */

#ifdef var_r_cr_plan_FFT_defined
#line 50 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
rfftw_plan r_cr_plan;
#endif /* var_r_cr_plan_FFT_defined */

#ifdef var_r_rc_plan_FFT_defined
#line 51 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
rfftw_plan r_rc_plan;
#endif /* var_r_rc_plan_FFT_defined */

#ifdef var_r_cr_plan_size_FFT_defined
#line 52 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
int r_cr_plan_size;
#endif /* var_r_cr_plan_size_FFT_defined */

#ifdef var_r_rc_plan_size_FFT_defined
#line 53 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
int r_rc_plan_size;
#endif /* var_r_rc_plan_size_FFT_defined */
};
/*! @decl array(array(float)) rFFT(array(int|float) real_input)
   *!
   *!   Returns the FFT of the input array.
   *!   The input must be real and the output is complex.
   *!   The output consists of an array. It's first element is the amplitudes and the
   *!   second element is the phases.
   *!
   *! @param real_input
   *!   The array of floats and/or ints to transform.
   *!
   *! @note
   *!   rIFFT(rFFT()) returns the input array scaled by n=sizeof(input array).
   *!   This is due to the nature of the DFT algorithm.
   *!
   *! @seealso
   *!   @[rIFFT()]
   */
  #define f_FFT_rFFT_defined
ptrdiff_t f_FFT_rFFT_fun_num = 0;
void f_FFT_rFFT(INT32 args) {
#line 72 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
struct array * myarray;
#line 72 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
if(args != 1) wrong_number_of_args_error("rFFT",args,1);
#line 72 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
if(Pike_sp[0-1].type != PIKE_T_ARRAY) SIMPLE_BAD_ARG_ERROR("rFFT",1,"array");
#line 72 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
debug_malloc_pass(myarray=Pike_sp[0-1].u.array);
{
      int i, j;

      if (args<1)
	SIMPLE_TOO_FEW_ARGS_ERROR("rFFT", 1);

      if (Pike_sp[-args].type==PIKE_T_ARRAY)
	{
	  struct array *a = Pike_sp[-args].u.array;
	  int n = a->size;
	  THIS->r_in = malloc(sizeof(fftw_real)*n);

	  for(i=0; i<n; i++)
	    {
	      if (a->item[i].type == PIKE_T_INT)
		THIS->r_in[i] = (fftw_real)a->item[i].u.integer;

	      if (a->item[i].type == PIKE_T_FLOAT)
		THIS->r_in[i] = (fftw_real)a->item[i].u.float_number;
	    };

	  THIS->r_out = malloc(sizeof(fftw_real)*n);

	  if (THIS->r_rc_plan_size != n)
	    {
	      if (THIS->r_rc_plan_size > 0)
		rfftw_destroy_plan(THIS->r_rc_plan);
	      THIS->r_rc_plan =
		rfftw_create_plan(n, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE);
	      THIS->r_rc_plan_size = n;
	    };
	  rfftw_one(THIS->r_rc_plan, THIS->r_in, THIS->r_out);

	  pop_n_elems(args);

	  i = (n+1)/2;
	  for(j=0; j<i; j++)
	    push_float((float)THIS->r_out[j]);

	  if (n%2==0) i++;

	  for(j=1; j<i; j++)
	    push_float((float)THIS->r_out[j]);

	  f_aggregate(n);


	  i = (n+1)/2;

	  push_float((float)0);
	  for(j=1; j<i; j++)
	    push_float((float)THIS->r_out[n-j]);

	  if (n%2==0)
	    push_float((float)0);

	  for(j=1; j<i; j++)
	    push_float((float) -(THIS->r_out[n-j]));

	  f_aggregate(n);
	  f_aggregate(2);

	  free(THIS->r_out);
	  THIS->r_out = NULL;
	  free(THIS->r_in);
	  THIS->r_in = NULL;
	}
      else
	pop_n_elems(args);
    }

  }
/*! @decl array(float) rIFFT(array(array(float)) input)
   *!
   *!   Returns the inverse FFT of the input array.
   *!   The input must be complex and guaranteed to generate a real output.
   *!
   *!   The input is an array. It's first element is the amplitudes and the
   *!   second element is the phases.
   *!
   *!   The output is an array of the real values for the iFFT.
   *! @param real_input
   *!   The array of floats and/or ints to transform.
   *!
   *! @note
   *!   rIFFT(rFFT()) returns the input array scaled by n=sizeof(input array).
   *!   This is due to the nature of the DFT algorithm.
   *!
   *! @seealso
   *!   @[rFFT()]
   */
  #define f_FFT_rIFFT_defined
ptrdiff_t f_FFT_rIFFT_fun_num = 0;
void f_FFT_rIFFT(INT32 args) {
#line 163 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
struct array * myarray;
#line 163 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
if(args != 1) wrong_number_of_args_error("rIFFT",args,1);
#line 163 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
if(Pike_sp[0-1].type != PIKE_T_ARRAY) SIMPLE_BAD_ARG_ERROR("rIFFT",1,"array");
#line 163 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
debug_malloc_pass(myarray=Pike_sp[0-1].u.array);
{
      int i, j=1;
      struct array *r;
      struct array *p;
      int n;

      if (myarray->item[0].type!=PIKE_T_ARRAY ||
	  myarray->item[1].type!=PIKE_T_ARRAY ||
	  myarray->item[0].u.array->size != myarray->item[1].u.array->size)
	Pike_error("Argument error!\n");

      r = myarray->item[0].u.array;
      p = myarray->item[1].u.array;
      n = r->size;

      THIS->r_in = malloc(sizeof(fftw_real)*n);
      memset(THIS->r_in,0,sizeof(fftw_real)*n);

      for(i=0; i<=n/2; i++)
	{
	  if (r->item[i].type == PIKE_T_INT)
	    THIS->r_in[i] = (fftw_real)r->item[i].u.integer;
	  else if (r->item[i].type == PIKE_T_FLOAT)
	    THIS->r_in[i] = (fftw_real)r->item[i].u.float_number;
	  else Pike_error("Invalid type in array!\n");
	}

      for(;i<n;i++)
	{
	  if (p->item[j].type == PIKE_T_INT)
	    THIS->r_in[n-j] = (fftw_real)p->item[j++].u.integer;
	  else if (p->item[j].type == PIKE_T_FLOAT)
	    THIS->r_in[n-j] = (fftw_real)p->item[j++].u.float_number;
	  else Pike_error("Invalid type in array!\n");
	}


      THIS->r_out = malloc(sizeof(fftw_real)*n);
      memset(THIS->r_out,0,sizeof(fftw_real)*n);

      if (THIS->r_cr_plan_size!=n)
	{
	  if (THIS->r_cr_plan_size > 0)
	    rfftw_destroy_plan(THIS->r_cr_plan);
	  THIS->r_cr_plan =
	    rfftw_create_plan(n, FFTW_COMPLEX_TO_REAL, FFTW_ESTIMATE);
	  THIS->r_cr_plan_size = n;
	}

      rfftw_one(THIS->r_cr_plan, THIS->r_in, THIS->r_out);

      pop_n_elems(args);

      for(i=0;i<n;i++)
	push_float((float)THIS->r_out[i]);

      f_aggregate(n);

      free(THIS->r_out);
      THIS->r_out = NULL;
      free(THIS->r_in);
      THIS->r_out = NULL;
    }

  }
/*! @decl void create(void|int n, void|int(0..1) exact)
   *!  Creates a new transform object. If n is specified, a plan
   *!  is created for transformations of n-size arrays.
   *!
   *! @param n
   *!   Size of the transform to be preformed. Note that the transform object
   *!   will be initialized for this size, but if an array of different size is
   *!   sent to the object, it will be reinitialized. This can be used to gain
   *!   preformace if all transforms will be of a given size.
   *!
   *! @param exact
   *!   If exact is 1, a "better" plan for the transform will be created. This
   *!   will take more time though. Use only if preformance is needed.
   */
  #define f_FFT_create_defined
ptrdiff_t f_FFT_create_fun_num = 0;
void f_FFT_create(INT32 args) {
#line 242 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
struct svalue * no;
#line 242 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
struct svalue * exact;
#line 242 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
if(args > 2) wrong_number_of_args_error("create",args,2);
if (args > 0) {
#line 242 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
no=Pike_sp+0-args; dmalloc_touch_svalue(Pike_sp+0-args);
} else no=0;
if (args > 1) {
#line 242 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
exact=Pike_sp+1-args; dmalloc_touch_svalue(Pike_sp+1-args);
} else exact=0;
#line 245 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
{
    int t = FFTW_ESTIMATE;
    int n = 0;
    switch(args)
      {
      case 2:
	if (Pike_sp[1-args].u.integer!=0)
	  t=FFTW_MEASURE;
      case 1:
	n = Pike_sp[-args].u.integer;
	break;
      case 0:
	break;
      default:
	Pike_error("Invalid number of arguments\n");
      }

    THIS->r_rc_plan_size = n;
    THIS->r_cr_plan_size = n;
    if (n>0)
      {
	THIS->r_rc_plan = rfftw_create_plan(n, FFTW_REAL_TO_COMPLEX, t);
	THIS->r_cr_plan = rfftw_create_plan(n, FFTW_COMPLEX_TO_REAL, t);
      }
    pop_n_elems(args);
  }

  }

#undef internal_init_FFT_defined
#define internal_init_FFT_defined

#undef FFT_event_handler_defined
#define FFT_event_handler_defined
static void init_FFT_struct(void)
#line 273 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
{
      THIS->r_in=NULL;
      THIS->r_out=NULL;
    }

  
#undef internal_exit_FFT_defined
#define internal_exit_FFT_defined

#undef FFT_event_handler_defined
#define FFT_event_handler_defined
static void exit_FFT_struct(void)
#line 279 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
{
      if (THIS->r_cr_plan_size>0)
	rfftw_destroy_plan(THIS->r_cr_plan);
      if (THIS->r_rc_plan_size>0)
	rfftw_destroy_plan(THIS->r_rc_plan);
      if (THIS->r_out!=NULL)
	free(THIS->r_out);
      if (THIS->r_out!=NULL)
	free(THIS->r_in);
    }

#ifdef FFT_event_handler_defined
static void FFT_event_handler(int ev) {
  switch(ev) {

#ifdef internal_init_FFT_defined
  case PROG_EVENT_INIT: init_FFT_struct(); break;

#endif /* internal_init_FFT_defined */

#ifdef internal_exit_FFT_defined
  case PROG_EVENT_EXIT: exit_FFT_struct(); break;

#endif /* internal_exit_FFT_defined */
  default: break; 
  }
}

#endif /* FFT_event_handler_defined */
/*! @endclass
 */

/*! @endmodule
 */

/*! @endmodule
 */

#line 300 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
#endif /* HAVE_RFFTW */

void init_math_transforms(void)
{
#ifdef HAVE_RFFTW
  
#ifdef class_FFT_defined

#ifdef PROG_FFT_ID
  START_NEW_PROGRAM_ID(FFT);
#else
  start_new_program();

#endif /* PROG_FFT_ID */

#ifndef tObjImpl_FFT

#undef tObjImpl_FFT
#define tObjImpl_FFT tObj

#endif /* tObjImpl_FFT */

#ifdef THIS_FFT

  FFT_storage_offset=ADD_STORAGE(struct FFT_struct);

#endif /* THIS_FFT */

#ifdef FFT_event_handler_defined
  pike_set_prog_event_callback(FFT_event_handler);

#endif /* FFT_event_handler_defined */

#ifdef f_FFT_rFFT_defined
#line 72 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
  f_FFT_rFFT_fun_num =
    ADD_FUNCTION2("rFFT", f_FFT_rFFT, tFunc(tArray,tArray), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_FFT_rFFT_defined */

#ifdef f_FFT_rIFFT_defined
#line 163 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
  f_FFT_rIFFT_fun_num =
    ADD_FUNCTION2("rIFFT", f_FFT_rIFFT, tFunc(tArray,tArray), 0, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_FFT_rIFFT_defined */

#ifdef f_FFT_create_defined
#line 242 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
  f_FFT_create_fun_num =
    ADD_FUNCTION2("create", f_FFT_create, tFunc(tOr(tVoid,"\10\200\0\0\0\177\377\377\377") tOr(tVoid,"\10\0\0\0\0\0\0\0\1"),tVoid), ID_STATIC, OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

#endif /* f_FFT_create_defined */
  FFT_program=end_program();
  FFT_program_fun_num=add_program_constant("FFT",FFT_program,0);

#endif /* class_FFT_defined */
#line 306 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
#endif /* HAVE_RFFTW */
}

void exit_math_transforms(void)
{
#ifdef HAVE_RFFTW
  
#ifdef class_FFT_defined
  if(FFT_program) {
    free_program(FFT_program);
    FFT_program=0;
  }

#endif /* class_FFT_defined */
#line 313 "/export/aphraelraid/home/peter/hack/Pike/7.6/src/modules/Math/transforms.cmod"
#endif /* HAVE_RFFTW */
}

