/*
  na_fftw3.c

  FFT using FFTW Ver.3 (www.fftw.org)

    (C) Takeshi Horinouchi
    NO WARRANTY.

*/

#include <ruby.h>
#include "narray.h"
#include <fftw3.h>

VALUE rb_mFFTW3;
VALUE mNumRu;

static VALUE
#ifdef FFTW3_HAS_SINGLE_SUPPORT
na_fftw3_double(int argc, VALUE *argv, VALUE self)
  /* to be called by na_fftw3 */
#else
na_fftw3(int argc, VALUE *argv, VALUE self)
  /* to be called directly */
#endif
{
  VALUE val, vdir;
  struct NARRAY *a1, *a2;
  int i, dir, *shape, *bucket;
  fftw_plan p;
  fftw_complex *in, *out;
  volatile VALUE v1, v2;

  if (argc<2){
    rb_raise(rb_eArgError, "Usage: fft(narray, direction [,dim0,dim1,...])");
  }
  val = argv[0];
  vdir = argv[1];

  dir = NUM2INT(vdir);
  if ( dir != 1 && dir != -1 ){
    rb_raise(rb_eArgError, "direction should be 1 or -1");
  }
  v1 = na_cast_object(val, NA_DCOMPLEX);
  GetNArray(v1,a1);
  v2 = na_make_object( NA_DCOMPLEX, a1->rank, a1->shape, CLASS_OF(v1) );
  GetNArray(v2,a2);

  shape = ALLOCA_N(int, a2->rank);
  for (i=0; i<a2->rank; i++){
      shape[i] = a2->shape[a2->rank-1-i];
  }
  in = (fftw_complex*)a1->ptr;
  out = (fftw_complex*)a2->ptr;

  if (argc==2) {
      /* apply FFT to all dimensions */
      p = fftw_plan_dft( a2->rank, shape, 
			 in, out, dir, FFTW_ESTIMATE );
  } else {
      /* apply FFT to selected dimensions (by using the Guru interface) */
      { /* introduce a new scope for additonal local variables */
	  int fft_rank, howmany_rank, j, jf, je, dim;
	  fftw_iodim *fft_dims, *howmany_dims;
	  int *dimids;
	  fft_rank = argc - 2;
	  fft_dims = ALLOCA_N(fftw_iodim, fft_rank);
	  dimids = ALLOCA_N(int, fft_rank);
	  howmany_rank = fft_rank + 1;
	  howmany_dims = ALLOCA_N(fftw_iodim, howmany_rank);
	  
	  for (i=2;i<argc;i++){
	      dim = NUM2INT(argv[i]);
	      if (dim<0) dim += a2->rank;  /* negative: count from the end */
	      if (dim<0 || dim>=a2->rank){
		  rb_raise(rb_eArgError, "dimension < 0 or >= rank");
	      }
	      dimids[i-2] = a2->rank - 1 - dim;
	      if ( i>2 && dimids[i-2] == dimids[i-3] ){
		  rb_raise(rb_eArgError, "redundant -- a same dimension is reppeated");
	      }
	  }
	  
	  /* bukcet sort in increasing order */
	  bucket = ALLOCA_N(int,a2->rank);
	  for(j=0; j<a2->rank; j++) bucket[j] = 0; /* initialize */
	  for(i=0; i<fft_rank; i++) bucket[ dimids[i] ] = 1;
	  for(j=0,i=0; j<a2->rank; j++) {
	      if (bucket[j]==1){
		  dimids[i] = j;
		  i++;
	      }
	  }

	  for(j=0; j<fft_rank; j++){
	      fft_dims[j].n = shape[ dimids[j] ];
	      fft_dims[j].is = 1;
	      for (i=dimids[j]+1 ; i<a2->rank ; i++){
		  fft_dims[j].is *= shape[i];
	      }
	      fft_dims[j].os = fft_dims[j].is;
	      /* printf("fft_ %d  n:%d  is:%d\n",j,
		       fft_dims[j].n,fft_dims[j].is);*/
	  }
	  for(j=0; j<=fft_rank; j++){
	      howmany_dims[j].n = 1;
	      jf = (j==0) ? 0 : (dimids[j-1]+1) ;
	      je = (j==fft_rank) ? a2->rank : (dimids[j]) ;
	      for (i=jf; i<je; i++){
		  howmany_dims[j].n *= shape[i];
	      }
	      howmany_dims[j].is = 1;
	      if (j<fft_rank){
		  for (i=dimids[j]; i<a2->rank; i++){
		      howmany_dims[j].is *= shape[i];
		  }
	      }
	      howmany_dims[j].os = howmany_dims[j].is;
	      /* printf("how_ %d  n:%d  is:%d\n",j,
		        howmany_dims[j].n,howmany_dims[j].is); */
	  }

	  p = fftw_plan_guru_dft( fft_rank, fft_dims, 
				  howmany_rank, howmany_dims,
				  in, out, dir, FFTW_ESTIMATE );

      }
  }

  fftw_execute(p);
  fftw_destroy_plan(p);

  return v2;
}

#ifdef FFTW3_HAS_SINGLE_SUPPORT

/* sourse code generation of na_fftw3_float:
  Copy na_fftw3_double, and replace 
     fftw --> fftwf
     DCOMPLEX --> SCOMPLEX
 */
static VALUE
na_fftw3_float(int argc, VALUE *argv, VALUE self)
{
  VALUE val, vdir;
  struct NARRAY *a1, *a2;
  int i, dir, *shape, *bucket;
  fftwf_plan p;
  fftwf_complex *in, *out;
  volatile VALUE v1, v2;

  if (argc<2){
    rb_raise(rb_eArgError, "Usage: fft(narray, direction [,dim0,dim1,...])");
  }
  val = argv[0];
  vdir = argv[1];

  dir = NUM2INT(vdir);
  if ( dir != 1 && dir != -1 ){
    rb_raise(rb_eArgError, "direction should be 1 or -1");
  }
  v1 = na_cast_object(val, NA_SCOMPLEX);
  GetNArray(v1,a1);
  v2 = na_make_object( NA_SCOMPLEX, a1->rank, a1->shape, CLASS_OF(v1) );
  GetNArray(v2,a2);

  shape = ALLOCA_N(int, a2->rank);
  for (i=0; i<a2->rank; i++){
      shape[i] = a2->shape[a2->rank-1-i];
  }
  in = (fftwf_complex*)a1->ptr;
  out = (fftwf_complex*)a2->ptr;

  if (argc==2) {
      /* apply FFT to all dimensions */
      p = fftwf_plan_dft( a2->rank, shape, 
			 in, out, dir, FFTW_ESTIMATE );
  } else {
      /* apply FFT to selected dimensions (by using the Guru interface) */
      { /* introduce a new scope for additonal local variables */
	  int fft_rank, howmany_rank, j, jf, je, dim;
	  fftw_iodim *fft_dims, *howmany_dims;
	  int *dimids;
	  fft_rank = argc - 2;
	  fft_dims = ALLOCA_N(fftw_iodim, fft_rank);
	  dimids = ALLOCA_N(int, fft_rank);
	  howmany_rank = fft_rank + 1;
	  howmany_dims = ALLOCA_N(fftw_iodim, howmany_rank);
	  
	  for (i=2;i<argc;i++){
	      dim = NUM2INT(argv[i]);
	      if (dim<0) dim += a2->rank;  /* negative: count from the end */
	      if (dim<0 || dim>=a2->rank){
		  rb_raise(rb_eArgError, "dimension < 0 or >= rank");
	      }
	      dimids[i-2] = a2->rank - 1 - dim;
	      if ( i>2 && dimids[i-2] == dimids[i-3] ){
		  rb_raise(rb_eArgError, "redundant -- a same dimension is reppeated");
	      }
	  }
	  
	  /* bukcet sort in increasing order */
	  bucket = ALLOCA_N(int,a2->rank);
	  for(j=0; j<a2->rank; j++) bucket[j] = 0; /* initialize */
	  for(i=0; i<fft_rank; i++) bucket[ dimids[i] ] = 1;
	  for(j=0,i=0; j<a2->rank; j++) {
	      if (bucket[j]==1){
		  dimids[i] = j;
		  i++;
	      }
	  }

	  for(j=0; j<fft_rank; j++){
	      fft_dims[j].n = shape[ dimids[j] ];
	      fft_dims[j].is = 1;
	      for (i=dimids[j]+1 ; i<a2->rank ; i++){
		  fft_dims[j].is *= shape[i];
	      }
	      fft_dims[j].os = fft_dims[j].is;
	      /* printf("fft_ %d  n:%d  is:%d\n",j,
		       fft_dims[j].n,fft_dims[j].is);*/
	  }
	  for(j=0; j<=fft_rank; j++){
	      howmany_dims[j].n = 1;
	      jf = (j==0) ? 0 : (dimids[j-1]+1) ;
	      je = (j==fft_rank) ? a2->rank : (dimids[j]) ;
	      for (i=jf; i<je; i++){
		  howmany_dims[j].n *= shape[i];
	      }
	      howmany_dims[j].is = 1;
	      if (j<fft_rank){
		  for (i=dimids[j]; i<a2->rank; i++){
		      howmany_dims[j].is *= shape[i];
		  }
	      }
	      howmany_dims[j].os = howmany_dims[j].is;
	      /* printf("how_ %d  n:%d  is:%d\n",j,
		        howmany_dims[j].n,howmany_dims[j].is); */
	  }

	  p = fftwf_plan_guru_dft( fft_rank, fft_dims, 
				  howmany_rank, howmany_dims,
				  in, out, dir, FFTW_ESTIMATE );

      }
  }

  fftwf_execute(p);
  fftwf_destroy_plan(p);

  return v2;
}

/*
 * call-seq:
 *   FFTW3.fft(narray, dir [, dim, dim, ...])
 * 
 * Conducts complex FFT (unnormalized). You can use more user-friendly wrappers
 * fft_fw and fft_bk.
 * 
 * ARGUMENTS
 * * narray (NArray or NArray-compatible Array) : array to be
 *   transformed. If real, coerced to complex before transformation.
 *   If narray is single-precision and the single-precision
 *   version of FFTW3 is installed (before installing this module),
 *   this method does a single-precision transform. 
 *   Otherwise, a double-precision transform is used.
 * * dir (FORWARD (which is simply equal to -1; 
 *   referable as NumRu::FFTW3::FORWARD) or BACKWARD
 *   (which is simply 1) ) : the direction of FFT,
 *   forward if FORWARD and backward if BACKWARD.
 * * optional 3rd, 4th,... arguments (Integer) : Specifies dimensions 
 *   to apply FFT. For example, if 0, the first dimension is
 *   transformed (1D FFT); If -1, the last dimension is used (1D FFT);
 *   If 0,2,4, the first, third, and fifth dimensions
 *   are transformed (3D FFT); If entirely omitted, ALL DIMENSIONS
 *   ARE SUBJECT TO FFT, so 3D FFT is done with a 3D array.
 * 
 * RETURN VALUE
 * * a complex NArray
 * 
 * NOTE
 * * As in FFTW, return value is NOT normalized. Thus, a consecutive
 *   forward and backward transform would multiply the size of
 *   data used for transform. You can normalize, for example,
 *   the forward transform FFTW.fft(narray, -1, 0, 1)
 *   (FFT regarding the first (dim 0) & second (dim 1) dimensions) by
 *   dividing with (narray.shape[0]*narray.shape[1]). Likewise,
 *   the result of FFTW.fft(narray, -1) (FFT for all dimensions)
 *   can be normalized by narray.length.
 */
static VALUE
na_fftw3(int argc, VALUE *argv, VALUE self)
{
  VALUE val;
  volatile VALUE v1;
  struct NARRAY *a1;

  if (argc<2){
    rb_raise(rb_eArgError, "Usage: fft(narray, direction [,dim0,dim1,...])");
  }
  val = argv[0];
  v1 = na_to_narray(val);
  GetNArray(v1,a1);
  if(a1->type <= NA_SFLOAT || a1->type == NA_SCOMPLEX ){
      return( na_fftw3_float(argc, argv, self) );
  } else {
      return( na_fftw3_double(argc, argv, self) );
  }

}

#endif

static VALUE
#ifdef FFTW3_HAS_SINGLE_SUPPORT
na_fftw3_r2r_double(int argc, VALUE *argv, VALUE self)
  /* to be called by na_fftw3_r2r */
#else
na_fftw3_r2r(int argc, VALUE *argv, VALUE self)
  /* to be called directly */
#endif
{
  VALUE val, vkinds;
  struct NARRAY *a1, *a2;
  int i, *shape, *bucket, len;
  fftw_r2r_kind *kinds;
  fftw_plan p;
  double *in, *out;
  volatile VALUE v1, v2;

  if (argc<2){
    rb_raise(rb_eArgError, "Usage: fft_r2r(narray, kinds [,dim0,dim1,...])");
  }
  val = argv[0];
  vkinds = argv[1];

  v1 = na_cast_object(val, NA_DFLOAT);
  GetNArray(v1,a1);
  v2 = na_make_object( NA_DFLOAT, a1->rank, a1->shape, CLASS_OF(v1) );
  GetNArray(v2,a2);

  shape = ALLOCA_N(int, a2->rank);
  for (i=0; i<a2->rank; i++){
      shape[i] = a2->shape[a2->rank-1-i];
  }
  in = (double*)a1->ptr;
  out = (double*)a2->ptr;

  switch (TYPE(vkinds)) {
  case T_ARRAY:
      len = RARRAY_LEN(vkinds);
      kinds = ALLOCA_N(fftw_r2r_kind, len);
      for (i = 0; i < len; i++) {
	  kinds[i] = NUM2INT(RARRAY_PTR(vkinds)[len-1-i]);//column- to row-major
      }
      break;
  case T_FIXNUM:
      if (argc == 2) {
	  len = a1->rank;
      } else {
	  len = argc-2;
      }
      kinds = ALLOCA_N(fftw_r2r_kind, len);
      for (i = 0; i < len; i++) {
	  kinds[i] = NUM2INT(vkinds);
      }
      break;
  default:
      rb_raise(rb_eTypeError, "unsupported kinds type");
      break;
  }

  for (i = 0; i < len; i++) {
      if ( kinds[i] < FFTW_R2HC || kinds[i] > FFTW_RODFT11 ){
	  rb_raise(rb_eArgError, "unsupported kind (%d).", kinds[i]);
      }
  }

  if (argc==2) {
      /* apply FFT to all dimensions */
      p = fftw_plan_r2r( a2->rank, shape,
			 in, out, kinds, FFTW_ESTIMATE );
  } else {
      /* apply FFT to selected dimensions (by using the Guru interface) */
      { /* introduce a new scope for additonal local variables */
	  int fft_rank, howmany_rank, j, jf, je, dim;
	  fftw_iodim *fft_dims, *howmany_dims;
	  int *dimids;
	  fft_rank = argc - 2;
	  fft_dims = ALLOCA_N(fftw_iodim, fft_rank);
	  dimids = ALLOCA_N(int, fft_rank);
	  howmany_rank = fft_rank + 1;
	  howmany_dims = ALLOCA_N(fftw_iodim, howmany_rank);

	  for (i=2;i<argc;i++){
	      dim = NUM2INT(argv[i]);
	      if (dim<0) dim += a2->rank;  /* negative: count from the end */
	      if (dim<0 || dim>=a2->rank){
		  rb_raise(rb_eArgError, "dimension < 0 or >= rank");
	      }
	      dimids[i-2] = a2->rank - 1 - dim;
	      if ( i>2 && dimids[i-2] == dimids[i-3] ){
		  rb_raise(rb_eArgError, "redundant -- a same dimension is reppeated");
	      }
	  }

	  /* bukcet sort in increasing order */
	  bucket = ALLOCA_N(int,a2->rank);
	  for(j=0; j<a2->rank; j++) bucket[j] = 0; /* initialize */
	  for(i=0; i<fft_rank; i++) bucket[ dimids[i] ] = 1;
	  for(j=0,i=0; j<a2->rank; j++) {
	      if (bucket[j]==1){
		  dimids[i] = j;
		  i++;
	      }
	  }

	  for(j=0; j<fft_rank; j++){
	      fft_dims[j].n = shape[ dimids[j] ];
	      fft_dims[j].is = 1;
	      for (i=dimids[j]+1 ; i<a2->rank ; i++){
		  fft_dims[j].is *= shape[i];
	      }
	      fft_dims[j].os = fft_dims[j].is;
	      /* printf("fft_ %d  n:%d  is:%d\n",j,
		       fft_dims[j].n,fft_dims[j].is);*/
	  }
	  for(j=0; j<=fft_rank; j++){
	      howmany_dims[j].n = 1;
	      jf = (j==0) ? 0 : (dimids[j-1]+1) ;
	      je = (j==fft_rank) ? a2->rank : (dimids[j]) ;
	      for (i=jf; i<je; i++){
		  howmany_dims[j].n *= shape[i];
	      }
	      howmany_dims[j].is = 1;
	      if (j<fft_rank){
		  for (i=dimids[j]; i<a2->rank; i++){
		      howmany_dims[j].is *= shape[i];
		  }
	      }
	      howmany_dims[j].os = howmany_dims[j].is;
	      /* printf("how_ %d  n:%d  is:%d\n",j,
		        howmany_dims[j].n,howmany_dims[j].is); */
	  }

	  p = fftw_plan_guru_r2r( fft_rank, fft_dims,
				  howmany_rank, howmany_dims,
				  in, out, kinds, FFTW_ESTIMATE );

      }
  }

  fftw_execute(p);
  fftw_destroy_plan(p);

  return v2;
}

#ifdef FFTW3_HAS_SINGLE_SUPPORT
static VALUE
na_fftw3_r2r_float(int argc, VALUE *argv, VALUE self)
  /* to be called by na_fftw3_r2r */
{
  VALUE val, vkinds;
  struct NARRAY *a1, *a2;
  int i, *shape, *bucket, len;
  fftwf_r2r_kind *kinds;
  fftwf_plan p;
  float *in, *out;
  volatile VALUE v1, v2;

  if (argc<2){
    rb_raise(rb_eArgError, "Usage: fft_r2r(narray, kinds [,dim0,dim1,...])");
  }
  val = argv[0];
  vkinds = argv[1];

  v1 = na_cast_object(val, NA_SFLOAT);
  GetNArray(v1,a1);
  v2 = na_make_object( NA_SFLOAT, a1->rank, a1->shape, CLASS_OF(v1) );
  GetNArray(v2,a2);

  shape = ALLOCA_N(int, a2->rank);
  for (i=0; i<a2->rank; i++){
      shape[i] = a2->shape[a2->rank-1-i];
  }
  in = (float*)a1->ptr;
  out = (float*)a2->ptr;

  switch (TYPE(vkinds)) {
  case T_ARRAY:
      len = RARRAY_LEN(vkinds);
      kinds = ALLOCA_N(fftwf_r2r_kind, len);
      for (i = 0; i < len; i++) {
	  kinds[i] = NUM2INT(RARRAY_PTR(vkinds)[len-1-i]);//column- to row-major
      }
      break;
  case T_FIXNUM:
      if (argc == 2) {
	  len = a1->rank;
      } else {
	  len = argc-2;
      }
      kinds = ALLOCA_N(fftwf_r2r_kind, len);
      for (i = 0; i < len; i++) {
	  kinds[i] = NUM2INT(vkinds);
      }
      break;
  default:
      rb_raise(rb_eTypeError, "unsupported kinds type");
      break;
  }

  for (i = 0; i < len; i++) {
      if ( kinds[i] < FFTW_R2HC || kinds[i] > FFTW_RODFT11 ){
	  rb_raise(rb_eArgError, "unsupported kind (%d).", kinds[i]);
      }
  }

  if (argc==2) {
      /* apply FFT to all dimensions */
      p = fftwf_plan_r2r( a2->rank, shape,
			 in, out, kinds, FFTW_ESTIMATE );
  } else {
      /* apply FFT to selected dimensions (by using the Guru interface) */
      { /* introduce a new scope for additonal local variables */
	  int fft_rank, howmany_rank, j, jf, je, dim;
	  fftwf_iodim *fft_dims, *howmany_dims;
	  int *dimids;
	  fft_rank = argc - 2;
	  fft_dims = ALLOCA_N(fftwf_iodim, fft_rank);
	  dimids = ALLOCA_N(int, fft_rank);
	  howmany_rank = fft_rank + 1;
	  howmany_dims = ALLOCA_N(fftwf_iodim, howmany_rank);

	  for (i=2;i<argc;i++){
	      dim = NUM2INT(argv[i]);
	      if (dim<0) dim += a2->rank;  /* negative: count from the end */
	      if (dim<0 || dim>=a2->rank){
		  rb_raise(rb_eArgError, "dimension < 0 or >= rank");
	      }
	      dimids[i-2] = a2->rank - 1 - dim;
	      if ( i>2 && dimids[i-2] == dimids[i-3] ){
		  rb_raise(rb_eArgError, "redundant -- a same dimension is reppeated");
	      }
	  }

	  /* bukcet sort in increasing order */
	  bucket = ALLOCA_N(int,a2->rank);
	  for(j=0; j<a2->rank; j++) bucket[j] = 0; /* initialize */
	  for(i=0; i<fft_rank; i++) bucket[ dimids[i] ] = 1;
	  for(j=0,i=0; j<a2->rank; j++) {
	      if (bucket[j]==1){
		  dimids[i] = j;
		  i++;
	      }
	  }

	  for(j=0; j<fft_rank; j++){
	      fft_dims[j].n = shape[ dimids[j] ];
	      fft_dims[j].is = 1;
	      for (i=dimids[j]+1 ; i<a2->rank ; i++){
		  fft_dims[j].is *= shape[i];
	      }
	      fft_dims[j].os = fft_dims[j].is;
	      /* printf("fft_ %d  n:%d  is:%d\n",j,
		       fft_dims[j].n,fft_dims[j].is);*/
	  }
	  for(j=0; j<=fft_rank; j++){
	      howmany_dims[j].n = 1;
	      jf = (j==0) ? 0 : (dimids[j-1]+1) ;
	      je = (j==fft_rank) ? a2->rank : (dimids[j]) ;
	      for (i=jf; i<je; i++){
		  howmany_dims[j].n *= shape[i];
	      }
	      howmany_dims[j].is = 1;
	      if (j<fft_rank){
		  for (i=dimids[j]; i<a2->rank; i++){
		      howmany_dims[j].is *= shape[i];
		  }
	      }
	      howmany_dims[j].os = howmany_dims[j].is;
	      /* printf("how_ %d  n:%d  is:%d\n",j,
		        howmany_dims[j].n,howmany_dims[j].is); */
	  }

	  p = fftwf_plan_guru_r2r( fft_rank, fft_dims,
				  howmany_rank, howmany_dims,
				  in, out, kinds, FFTW_ESTIMATE );

      }
  }

  fftwf_execute(p);
  fftwf_destroy_plan(p);

  return v2;
}

/*
 * call-seq:
 *   FFTW3.fft_r2r(narray, kind [, dim, dim, ...])
 * 
 * Conducts real FFT (unnormalized).
 * 
 * ARGUMENTS
 * * narray (NArray or NArray-compatible Array) : array to be
 *   transformed. Cannot be complex.
 * * kind : specifies the kind of FFT. One of the following:
 *   R2HC, HC2R, DHT, REDFT00, REDFT01, REDFT10, REDFT11, RODFT00, RODFT01, 
 *   RODFT10, or RODFT11 (referable as NumRu::FFTW3::REDFT10). See Ch.2 of
 *   http://www.fftw.org/fftw3_doc/ (http://www.fftw.org/fftw3.pdf).
 * * optional 3rd, 4th,... arguments (Integer) : See FFTW3.fft.
 *   
 * 
 * RETURN VALUE
 * * a real NArray
 * 
 * NOTE
 * * As in FFTW, return value is NOT normalized, and the length needed
 *   normalize is different for each kind.
 */
static VALUE
na_fftw3_r2r(int argc, VALUE *argv, VALUE self)
{
  VALUE val;
  volatile VALUE v1;
  struct NARRAY *a1;

  if (argc<2){
    rb_raise(rb_eArgError, "Usage: fft_r2r(narray, kinds [,dim0,dim1,...])");
  }
  val = argv[0];
  v1 = na_to_narray(val);
  GetNArray(v1,a1);
  if(a1->type <= NA_SFLOAT || a1->type == NA_SCOMPLEX ){
      return( na_fftw3_r2r_float(argc, argv, self) );
  } else {
      return( na_fftw3_r2r_double(argc, argv, self) );
  }

}

#endif

void
 Init_fftw3()
{
  mNumRu = rb_define_module("NumRu");
  rb_mFFTW3 = rb_define_module_under(mNumRu, "FFTW3");
  rb_define_module_function(rb_mFFTW3, "fft", na_fftw3, -1);
  rb_define_module_function(rb_mFFTW3, "fft_r2r", na_fftw3_r2r, -1);
  /* Specifier of forward complex FFT. Integer equal to -1. */
#ifdef NARRAY_BIGMEM
  rb_define_const(rb_mFFTW3, "SUPPORT_BIGMEM", Qtrue);
#else
  rb_define_const(rb_mFFTW3, "SUPPORT_BIGMEM", Qfalse);
#endif
  rb_define_const(rb_mFFTW3, "FORWARD",  INT2NUM(FFTW_FORWARD));
  /* Specifier of backward complex FFT. Integer equal to 1. */
  rb_define_const(rb_mFFTW3, "BACKWARD", INT2NUM(FFTW_BACKWARD));
  /* Specifier of real FFT kind. real to "halfcomplex" */
  rb_define_const(rb_mFFTW3, "R2HC", INT2NUM(FFTW_R2HC));
  /* Specifier of real FFT kind. "halfcomplex" to real */
  rb_define_const(rb_mFFTW3, "HC2R", INT2NUM(FFTW_HC2R));
  /* Specifier of real FFT kind. Discrete Hartley Transform */
  rb_define_const(rb_mFFTW3, "DHT", INT2NUM(FFTW_DHT));
  /* Specifier of real FFT kind. cosine (even) transform; 
     logical data length 2*(n-1); inverse is itself  */
  rb_define_const(rb_mFFTW3, "REDFT00", INT2NUM(FFTW_REDFT00));
  /* Specifier of real FFT kind. cosine (even) transform;
   * logical data length 2*n; inverse is REDFT10 */
  rb_define_const(rb_mFFTW3, "REDFT01", INT2NUM(FFTW_REDFT01));
  /* Specifier of real FFT kind. cosine (even) transform;
   * logical data length 2*n; inverse is REDFT01 */
  rb_define_const(rb_mFFTW3, "REDFT10", INT2NUM(FFTW_REDFT10));
  /* Specifier of real FFT kind. cosine (even) transform;
   * logical data length 2*n; inverse is itself */
  rb_define_const(rb_mFFTW3, "REDFT11", INT2NUM(FFTW_REDFT11));
  /* Specifier of real FFT kind. sine (odd) transform;
   * logical data length 2*(n+1); inverse is itself */
  rb_define_const(rb_mFFTW3, "RODFT00", INT2NUM(FFTW_RODFT00));
  /* Specifier of real FFT kind. sine (odd) transform;
   * logical data length 2*n; inverse is RODFT10 */
  rb_define_const(rb_mFFTW3, "RODFT01", INT2NUM(FFTW_RODFT01));
  /* Specifier of real FFT kind. sine (odd) transform;
   * logical data length 2*n; inverse is RODFT01 */
  rb_define_const(rb_mFFTW3, "RODFT10", INT2NUM(FFTW_RODFT10));
  /* Specifier of real FFT kind. sine (odd) transform;
   * logical data length 2*n; inverse is itself */
  rb_define_const(rb_mFFTW3, "RODFT11", INT2NUM(FFTW_RODFT11));
}
