/**
 * \license This project is released under the BSD 2-clause license
 *
 * Copyright (C) 2014 Xevolver Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/* 計算ラッパ群 倍精度FORTRAN用*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef FFTW_ASL_INCLUDED
#include "fftw_asl.h"
#endif

#ifndef UTIL_INCLUDED
#include "util.h"
#endif

/* 倍精度Fortran */
void dfftw_execute_(struct dfftw_plan **plan)
{
  struct dfftw_plan *plan_p = *plan;
  /* asl_wk:作業配列、asl_ifax:基数分け情報、asl_trigs:三角関数テーブル */
  /* 配列の大きさはASLの3次元複素フーリエ変換に対応。詳細はマニュアル参照 */
  Complex16 asl_wk[plan_p->asl_n1 * plan_p->asl_n2 * plan_p->asl_n3];
  Int32 asl_ifax[60];
  double asl_trigs[2 * (plan_p->asl_n1 + plan_p->asl_n2 + plan_p->asl_n3)];
  Int32    asl_err;                            /* エラーインディケータ */
  Int32    asl_isw;    /* 変換の方向 0:初期化のみ、1:順変換、-1:逆変換 */

  /* プランが生成されていれば asl_flags は絶対に -1 (何も行わない) */
  if (plan_p->asl_flags != NOEFFECT)
  {
    fprintf(stderr, "Illegal PLAN\n");
    exit(1);
  }

  /* ASLとFFTWの変換の方向を対応させる */
  asl_isw = __FFTWR_fftw_to_asl_flag(plan_p->asl_fbflag);

  switch (plan_p->asl_plan_type)
  {
  case DFFTW_PLAN_DFT_1D :
    /* 複素1次元 */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (plan_p->asl_outdt != plan_p->asl_indt )
    {
      memmove(plan_p->asl_outdt, plan_p->asl_indt, plan_p->asl_n1*sizeof(Complex16));
    }
    zfc1fb_(&plan_p->asl_n1, plan_p->asl_outdt, &plan_p->asl_n1, &asl_isw,
             asl_ifax, asl_trigs,asl_wk, &asl_err);
    break ;

  case DFFTW_PLAN_DFT_2D :
    /* 複素2次元 */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (plan_p->asl_outdt != plan_p->asl_indt )
    {
      memmove(plan_p->asl_outdt, plan_p->asl_indt, plan_p->asl_n1*
                                                   plan_p->asl_n2*sizeof(Complex16));
    }
    zfc2fb_(&plan_p->asl_n1, &plan_p->asl_n2, plan_p->asl_outdt, &plan_p->asl_n1,
             &plan_p->asl_n2, &asl_isw, asl_ifax, asl_trigs, asl_wk,&asl_err);
    break ;

  case DFFTW_PLAN_DFT_3D :
    /* 複素3次元 */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (plan_p->asl_outdt != plan_p->asl_indt )
    {
      memmove(plan_p->asl_outdt, plan_p->asl_indt, plan_p->asl_n1*
                                                   plan_p->asl_n2*
                                                   plan_p->asl_n3*sizeof(Complex16));
    }
    zfc3fb_(&plan_p->asl_n1, &plan_p->asl_n2, &plan_p->asl_n3, plan_p->asl_outdt,
             &plan_p->asl_n1, &plan_p->asl_n2, &plan_p->asl_n3, &asl_isw, asl_ifax,
             asl_trigs, asl_wk,&asl_err);
    break ;

  default :
    fprintf(stderr, "Illegal PLANt\n");
    exit(1);
  }

  __FFTWR_check_asl_error(asl_err);

  return ;
}
#ifdef PHASE3D
#define MAX(a, b) ((a) > (b) ? (a) : (b))

/* dfftw_plan_dft_r2c_1d_, dfftw_plan_dft_r2c_3d_, dfftw_plan_many_dft_r2c_で生成されたプランのexecuter */
/* 他のplannerで生成されたplanが指定された場合はその旨を出力してexit          */
void dfftw_execute_dft_r2c_(struct dfftw_plan **plan, double *indt, double *outdt)
{
  struct dfftw_plan *plan_p = *plan;
  /* asl_wk:作業配列、asl_ifax:基数分け情報、asl_trigs:三角関数テーブル */
  /* 配列の大きさはASLの3次元複素フーリエ変換に対応。詳細はマニュアル参照 */
  Int32 asl_ldx, asl_size, asl_wksize, asl_trigssize;
  asl_ldx = (plan_p->asl_n1) + 1 + (plan_p->asl_n1 + 1)%2; /* 配列サイズ、要素数が偶数なら+2、奇数なら+1 */
  if(plan_p->asl_plan_type == DFFTW_PLAN_MANY_DFT_R2C) {
     /* 多重FFTの場合 */
     /* 順変換の場合は入力側が実数 */
     asl_size      = MAX(plan_p->asl_n1,plan_p->asl_idist)*MAX(plan_p->asl_howmany,plan_p->asl_istride);
     asl_wksize    = asl_ldx * plan_p->asl_howmany;
     asl_trigssize = plan_p->asl_n1;
  } else {
     /* 非多重FFTの場合 */
     asl_size      = asl_ldx * plan_p->asl_n2 * plan_p->asl_n3;
     asl_wksize    = asl_ldx * plan_p->asl_n2 * plan_p->asl_n3;
     asl_trigssize = plan_p->asl_n1 + 2*(plan_p->asl_n2 + plan_p->asl_n3);
  }
  double asl_wk[asl_wksize];
  Int32 asl_ifax[60];
  double asl_trigs[asl_trigssize];
  Int32    asl_err; /* エラーインディケータ */
  Int32    asl_isw; /* 変換の方向 0:初期化のみ、1:順変換、-1:逆変換 */

  /* プランが生成されていれば asl_flags は絶対に -1 (何も行わない) */
  if (plan_p->asl_flags != NOEFFECT)
  {
    fprintf(stderr, "Illegal PLAN\n");
    exit(1);
  }

  switch (plan_p->asl_plan_type)
  {
  case DFFTW_PLAN_DFT_R2C_1D :
    /* FORTRAN倍精度実1次元離散フーリエ変換（順変換） */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (outdt != indt )
    {
      memmove(outdt, indt, asl_ldx*sizeof(double));
    }
    asl_isw = 1; /* 順変換 */
    dfr1fb_(&plan_p->asl_n1, outdt, &asl_ldx, &asl_isw, asl_ifax, asl_trigs, asl_wk, &asl_err);
    break ;

  case DFFTW_PLAN_DFT_R2C_3D :
    /* FORTRAN倍精度実3次元離散フーリエ変換（順変換） */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (outdt != indt )
    {
      memmove(outdt, indt, asl_ldx*plan_p->asl_n2*plan_p->asl_n3*sizeof(double));
    }
    asl_isw = 1; /* 順変換 */
    dfr3fb_(&plan_p->asl_n1, &plan_p->asl_n2, &plan_p->asl_n3, outdt,
            &asl_ldx, &plan_p->asl_n2, &plan_p->asl_n3, &asl_isw, asl_ifax,
            asl_trigs, asl_wk,&asl_err);
    break ;

  case DFFTW_PLAN_MANY_DFT_R2C :
    /* FORTRAN倍精度実多重離散フーリエ変換（順変換） */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (outdt != indt )
    {
      memmove(outdt, indt, asl_size*sizeof(double));
    }
    asl_isw = 1; /* 順変換 */
    dfrmfb_(&plan_p->asl_n1, &plan_p->asl_howmany, outdt, &plan_p->asl_istride,  &plan_p->asl_idist,
            &asl_isw, asl_ifax, asl_trigs, asl_wk, &asl_err);
    break ;

  default :
    fprintf(stderr, "Illegal PLAN\n");
    exit(1);
  }

  __FFTWR_check_asl_error(asl_err);

  return ;
}

/* dfftw_plan_dft_c2r_1d_, dfftw_plan_dft_c2r_3d_, dfftw_plan_many_dft_c2r_で生成されたプランのexecuter */
/* 他のplannerで生成されたplanが指定された場合はその旨を出力してexit          */
void dfftw_execute_dft_c2r_(struct dfftw_plan **plan, double *indt, double *outdt)
{
  struct dfftw_plan *plan_p = *plan;
  /* asl_wk:作業配列、asl_ifax:基数分け情報、asl_trigs:三角関数テーブル */
  /* 配列の大きさはASLの3次元複素フーリエ変換に対応。詳細はマニュアル参照 */
  Int32 asl_ldx, asl_size, asl_wksize, asl_trigssize;
  asl_ldx = (plan_p->asl_n1) + 1 + (plan_p->asl_n1 + 1)%2; /* 配列サイズ、要素数が偶数なら+2、奇数なら+1 */
  if(plan_p->asl_plan_type == DFFTW_PLAN_MANY_DFT_C2R) {
     /* 多重FFTの場合 */
     /* 逆変換の場合は出力側が実数 */
     asl_size      = MAX(plan_p->asl_n1,plan_p->asl_odist)*MAX(plan_p->asl_howmany,plan_p->asl_ostride);
     asl_wksize    = asl_ldx * plan_p->asl_howmany;
     asl_trigssize = plan_p->asl_n1;
  } else {
     /* 非多重FFTの場合 */
     asl_size      = asl_ldx * plan_p->asl_n2 * plan_p->asl_n3;
     asl_wksize    = asl_ldx * plan_p->asl_n2 * plan_p->asl_n3;
     asl_trigssize = plan_p->asl_n1 + 2*(plan_p->asl_n2 + plan_p->asl_n3);
  }
  double asl_wk[asl_wksize];
  Int32 asl_ifax[60];
  double asl_trigs[asl_trigssize];
  Int32    asl_err; /* エラーインディケータ */
  Int32    asl_isw; /* 変換の方向 0:初期化のみ、1:順変換、-1:逆変換 */

  /* プランが生成されていれば asl_flags は絶対に -1 (何も行わない) */
  if (plan_p->asl_flags != NOEFFECT)
  {
    fprintf(stderr, "Illegal PLAN\n");
    exit(1);
  }

  switch (plan_p->asl_plan_type)
  {
  case DFFTW_PLAN_DFT_C2R_1D :
    /* FORTRAN倍精度実1次元離散フーリエ変換（逆変換） */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (outdt != indt )
    {
      memmove(outdt, indt, asl_ldx*sizeof(double));
    }
    asl_isw = -1; /* 逆変換 */
    dfr1fb_(&plan_p->asl_n1, outdt, &asl_ldx, &asl_isw, asl_ifax, asl_trigs, asl_wk, &asl_err);
    break ;

  case DFFTW_PLAN_DFT_C2R_3D :
    /* FORTRAN倍精度実3次元離散フーリエ変換（逆変換） */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (outdt != indt )
    {
      memmove(outdt, indt, asl_ldx*plan_p->asl_n2*plan_p->asl_n3*sizeof(double));
    }
    asl_isw = -1; /* 逆変換 */
    dfr3fb_(&plan_p->asl_n1, &plan_p->asl_n2, &plan_p->asl_n3, outdt,
            &asl_ldx, &plan_p->asl_n2, &plan_p->asl_n3, &asl_isw, asl_ifax,
            asl_trigs, asl_wk,&asl_err);
    break ;

  case DFFTW_PLAN_MANY_DFT_C2R :
    /* FORTRAN倍精度実多重離散フーリエ変換（逆変換） */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (outdt != indt )
    {
      memmove(outdt, indt, asl_size*sizeof(double));
    }
    asl_isw = -1; /* 逆変換 */
    dfrmfb_(&plan_p->asl_n1, &plan_p->asl_howmany, outdt, &plan_p->asl_ostride,  &plan_p->asl_odist,
            &asl_isw, asl_ifax, asl_trigs, asl_wk, &asl_err);
    break ;

  default :
    fprintf(stderr, "Illegal PLAN\n");
    exit(1);
  }

  __FFTWR_check_asl_error(asl_err);

  return ;
}

/* dfftw_plan_many_dft_で生成されたプランのexecuter */
/* 他のplannerで生成されたplanが指定された場合はその旨を出力してexit                                    */
/* この関数は他のplanで呼び出される可能性が非常に高いと思われ注意が必要！                               */
void dfftw_execute_dft_(struct dfftw_plan **plan, double *indt, double *outdt)
{
  struct dfftw_plan *plan_p = *plan;
  /* asl_wk:作業配列、asl_ifax:基数分け情報、asl_trigs:三角関数テーブル */
  /* 配列の大きさはASLの3次元複素フーリエ変換に対応。詳細はマニュアル参照 */
  Int32 asl_size;
  /* 複素多重の場合 */
  asl_size = MAX(plan_p->asl_n1,plan_p->asl_idist)*MAX(plan_p->asl_howmany,plan_p->asl_istride);
  double asl_wk[2 * plan_p->asl_n1 * plan_p->asl_howmany]; /* 複素FFT対応のため*2（実際は複素数型） */
  Int32 asl_ifax[20];
  double asl_trigs[2 * plan_p->asl_n1]; /* 複素FFT対応のため*2（実際は複素数型） */
  Int32    asl_err; /* エラーインディケータ */
  Int32    asl_isw; /* 変換の方向 0:初期化のみ、1:順変換、-1:逆変換 */

  /* プランが生成されていれば asl_flags は絶対に -1 (何も行わない) */
  if (plan_p->asl_flags != NOEFFECT)
  {
    fprintf(stderr, "Illegal PLAN\n");
    exit(1);
  }

  switch (plan_p->asl_plan_type)
  {
  case DFFTW_PLAN_MANY_DFT :
    /* FORTRAN倍精度複素多重離散フーリエ変換 */
    /* ASLは入出力配列の区別がないので、事前にコピーしてから計算 */
    if (outdt != indt )
    {
      memmove(outdt, indt, asl_size*sizeof(Complex16));
    }
    /* ASLとFFTWの変換の方向を対応させる */
    asl_isw = __FFTWR_fftw_to_asl_flag(plan_p->asl_fbflag);
    zfcmfb_(&plan_p->asl_n1, &plan_p->asl_howmany, outdt, &plan_p->asl_istride,  &plan_p->asl_idist,
            &asl_isw, asl_ifax, asl_trigs, asl_wk, &asl_err);
    break ;

  default :
    fprintf(stderr, "Illegal PLAN\n");
    exit(1);
  }

  __FFTWR_check_asl_error(asl_err);

  return ;
}
#endif
