/*
 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* This file was automatically generated --- DO NOT EDIT */
/* Generated on Mon Mar  8 17:46:56 EST 1999 */

#include <fftw-int.h>
#include <fftw.h>

/* Generated by: ./genfft -magic-alignment-check -magic-twiddle-load-all -magic-variables 4 -magic-loopi -twiddleinv 32 */

/*
 * This function contains 434 FP additions, 208 FP multiplications,
 * (or, 340 additions, 114 multiplications, 94 fused multiply/add),
 * 90 stack variables, and 128 memory accesses
 */
static const fftw_real K555570233 = FFTW_KONST(+0.555570233019602224742830813948532874374937191);
static const fftw_real K831469612 = FFTW_KONST(+0.831469612302545237078788377617905756738560812);
static const fftw_real K980785280 = FFTW_KONST(+0.980785280403230449126182236134239036973933731);
static const fftw_real K195090322 = FFTW_KONST(+0.195090322016128267848284868477022240927691618);
static const fftw_real K923879532 = FFTW_KONST(+0.923879532511286756128183189396788286822416626);
static const fftw_real K382683432 = FFTW_KONST(+0.382683432365089771728459984030398866761344562);
static const fftw_real K707106781 = FFTW_KONST(+0.707106781186547524400844362104849039284835938);

/*
 * Generator Id's :
 * $Id: exprdag.ml,v 1.36 1999/02/19 17:22:11 athena Exp $
 * $Id: fft.ml,v 1.41 1999/02/19 17:22:13 athena Exp $
 * $Id: to_c.ml,v 1.24 1999/02/19 17:22:17 athena Exp $
 */

void fftwi_twiddle_32(fftw_complex *A, const fftw_complex *W, int iostride, int m, int dist)
{
     int i;
     fftw_complex *inout;
     inout = A;
     for (i = m; i > 0; i = i - 1, inout = inout + dist, W = W + 31) {
	  fftw_real tmp19;
	  fftw_real tmp387;
	  fftw_real tmp472;
	  fftw_real tmp486;
	  fftw_real tmp442;
	  fftw_real tmp456;
	  fftw_real tmp191;
	  fftw_real tmp303;
	  fftw_real tmp161;
	  fftw_real tmp403;
	  fftw_real tmp276;
	  fftw_real tmp316;
	  fftw_real tmp372;
	  fftw_real tmp400;
	  fftw_real tmp259;
	  fftw_real tmp319;
	  fftw_real tmp42;
	  fftw_real tmp455;
	  fftw_real tmp201;
	  fftw_real tmp304;
	  fftw_real tmp390;
	  fftw_real tmp437;
	  fftw_real tmp196;
	  fftw_real tmp305;
	  fftw_real tmp184;
	  fftw_real tmp401;
	  fftw_real tmp375;
	  fftw_real tmp404;
	  fftw_real tmp270;
	  fftw_real tmp317;
	  fftw_real tmp279;
	  fftw_real tmp320;
	  fftw_real tmp66;
	  fftw_real tmp395;
	  fftw_real tmp224;
	  fftw_real tmp312;
	  fftw_real tmp357;
	  fftw_real tmp396;
	  fftw_real tmp219;
	  fftw_real tmp311;
	  fftw_real tmp114;
	  fftw_real tmp410;
	  fftw_real tmp249;
	  fftw_real tmp323;
	  fftw_real tmp363;
	  fftw_real tmp407;
	  fftw_real tmp232;
	  fftw_real tmp326;
	  fftw_real tmp89;
	  fftw_real tmp393;
	  fftw_real tmp213;
	  fftw_real tmp309;
	  fftw_real tmp354;
	  fftw_real tmp392;
	  fftw_real tmp208;
	  fftw_real tmp308;
	  fftw_real tmp137;
	  fftw_real tmp408;
	  fftw_real tmp366;
	  fftw_real tmp411;
	  fftw_real tmp243;
	  fftw_real tmp324;
	  fftw_real tmp252;
	  fftw_real tmp327;
	  ASSERT_ALIGNED_DOUBLE();
	  {
	       fftw_real tmp1;
	       fftw_real tmp440;
	       fftw_real tmp6;
	       fftw_real tmp439;
	       fftw_real tmp12;
	       fftw_real tmp188;
	       fftw_real tmp17;
	       fftw_real tmp189;
	       ASSERT_ALIGNED_DOUBLE();
	       tmp1 = c_re(inout[0]);
	       tmp440 = c_im(inout[0]);
	       {
		    fftw_real tmp3;
		    fftw_real tmp5;
		    fftw_real tmp2;
		    fftw_real tmp4;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp3 = c_re(inout[16 * iostride]);
		    tmp5 = c_im(inout[16 * iostride]);
		    tmp2 = c_re(W[15]);
		    tmp4 = c_im(W[15]);
		    tmp6 = (tmp2 * tmp3) + (tmp4 * tmp5);
		    tmp439 = (tmp2 * tmp5) - (tmp4 * tmp3);
	       }
	       {
		    fftw_real tmp9;
		    fftw_real tmp11;
		    fftw_real tmp8;
		    fftw_real tmp10;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp9 = c_re(inout[8 * iostride]);
		    tmp11 = c_im(inout[8 * iostride]);
		    tmp8 = c_re(W[7]);
		    tmp10 = c_im(W[7]);
		    tmp12 = (tmp8 * tmp9) + (tmp10 * tmp11);
		    tmp188 = (tmp8 * tmp11) - (tmp10 * tmp9);
	       }
	       {
		    fftw_real tmp14;
		    fftw_real tmp16;
		    fftw_real tmp13;
		    fftw_real tmp15;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp14 = c_re(inout[24 * iostride]);
		    tmp16 = c_im(inout[24 * iostride]);
		    tmp13 = c_re(W[23]);
		    tmp15 = c_im(W[23]);
		    tmp17 = (tmp13 * tmp14) + (tmp15 * tmp16);
		    tmp189 = (tmp13 * tmp16) - (tmp15 * tmp14);
	       }
	       {
		    fftw_real tmp7;
		    fftw_real tmp18;
		    fftw_real tmp470;
		    fftw_real tmp471;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp7 = tmp1 + tmp6;
		    tmp18 = tmp12 + tmp17;
		    tmp19 = tmp7 + tmp18;
		    tmp387 = tmp7 - tmp18;
		    tmp470 = tmp12 - tmp17;
		    tmp471 = tmp440 - tmp439;
		    tmp472 = tmp470 + tmp471;
		    tmp486 = tmp471 - tmp470;
	       }
	       {
		    fftw_real tmp438;
		    fftw_real tmp441;
		    fftw_real tmp187;
		    fftw_real tmp190;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp438 = tmp188 + tmp189;
		    tmp441 = tmp439 + tmp440;
		    tmp442 = tmp438 + tmp441;
		    tmp456 = tmp441 - tmp438;
		    tmp187 = tmp1 - tmp6;
		    tmp190 = tmp188 - tmp189;
		    tmp191 = tmp187 - tmp190;
		    tmp303 = tmp187 + tmp190;
	       }
	  }
	  {
	       fftw_real tmp143;
	       fftw_real tmp272;
	       fftw_real tmp159;
	       fftw_real tmp257;
	       fftw_real tmp148;
	       fftw_real tmp273;
	       fftw_real tmp154;
	       fftw_real tmp256;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp140;
		    fftw_real tmp142;
		    fftw_real tmp139;
		    fftw_real tmp141;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp140 = c_re(inout[31 * iostride]);
		    tmp142 = c_im(inout[31 * iostride]);
		    tmp139 = c_re(W[30]);
		    tmp141 = c_im(W[30]);
		    tmp143 = (tmp139 * tmp140) + (tmp141 * tmp142);
		    tmp272 = (tmp139 * tmp142) - (tmp141 * tmp140);
	       }
	       {
		    fftw_real tmp156;
		    fftw_real tmp158;
		    fftw_real tmp155;
		    fftw_real tmp157;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp156 = c_re(inout[23 * iostride]);
		    tmp158 = c_im(inout[23 * iostride]);
		    tmp155 = c_re(W[22]);
		    tmp157 = c_im(W[22]);
		    tmp159 = (tmp155 * tmp156) + (tmp157 * tmp158);
		    tmp257 = (tmp155 * tmp158) - (tmp157 * tmp156);
	       }
	       {
		    fftw_real tmp145;
		    fftw_real tmp147;
		    fftw_real tmp144;
		    fftw_real tmp146;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp145 = c_re(inout[15 * iostride]);
		    tmp147 = c_im(inout[15 * iostride]);
		    tmp144 = c_re(W[14]);
		    tmp146 = c_im(W[14]);
		    tmp148 = (tmp144 * tmp145) + (tmp146 * tmp147);
		    tmp273 = (tmp144 * tmp147) - (tmp146 * tmp145);
	       }
	       {
		    fftw_real tmp151;
		    fftw_real tmp153;
		    fftw_real tmp150;
		    fftw_real tmp152;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp151 = c_re(inout[7 * iostride]);
		    tmp153 = c_im(inout[7 * iostride]);
		    tmp150 = c_re(W[6]);
		    tmp152 = c_im(W[6]);
		    tmp154 = (tmp150 * tmp151) + (tmp152 * tmp153);
		    tmp256 = (tmp150 * tmp153) - (tmp152 * tmp151);
	       }
	       {
		    fftw_real tmp149;
		    fftw_real tmp160;
		    fftw_real tmp274;
		    fftw_real tmp275;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp149 = tmp143 + tmp148;
		    tmp160 = tmp154 + tmp159;
		    tmp161 = tmp149 + tmp160;
		    tmp403 = tmp149 - tmp160;
		    tmp274 = tmp272 - tmp273;
		    tmp275 = tmp154 - tmp159;
		    tmp276 = tmp274 + tmp275;
		    tmp316 = tmp274 - tmp275;
	       }
	       {
		    fftw_real tmp370;
		    fftw_real tmp371;
		    fftw_real tmp255;
		    fftw_real tmp258;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp370 = tmp272 + tmp273;
		    tmp371 = tmp256 + tmp257;
		    tmp372 = tmp370 + tmp371;
		    tmp400 = tmp370 - tmp371;
		    tmp255 = tmp143 - tmp148;
		    tmp258 = tmp256 - tmp257;
		    tmp259 = tmp255 - tmp258;
		    tmp319 = tmp255 + tmp258;
	       }
	  }
	  {
	       fftw_real tmp24;
	       fftw_real tmp193;
	       fftw_real tmp40;
	       fftw_real tmp199;
	       fftw_real tmp29;
	       fftw_real tmp194;
	       fftw_real tmp35;
	       fftw_real tmp198;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp21;
		    fftw_real tmp23;
		    fftw_real tmp20;
		    fftw_real tmp22;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp21 = c_re(inout[4 * iostride]);
		    tmp23 = c_im(inout[4 * iostride]);
		    tmp20 = c_re(W[3]);
		    tmp22 = c_im(W[3]);
		    tmp24 = (tmp20 * tmp21) + (tmp22 * tmp23);
		    tmp193 = (tmp20 * tmp23) - (tmp22 * tmp21);
	       }
	       {
		    fftw_real tmp37;
		    fftw_real tmp39;
		    fftw_real tmp36;
		    fftw_real tmp38;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp37 = c_re(inout[12 * iostride]);
		    tmp39 = c_im(inout[12 * iostride]);
		    tmp36 = c_re(W[11]);
		    tmp38 = c_im(W[11]);
		    tmp40 = (tmp36 * tmp37) + (tmp38 * tmp39);
		    tmp199 = (tmp36 * tmp39) - (tmp38 * tmp37);
	       }
	       {
		    fftw_real tmp26;
		    fftw_real tmp28;
		    fftw_real tmp25;
		    fftw_real tmp27;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp26 = c_re(inout[20 * iostride]);
		    tmp28 = c_im(inout[20 * iostride]);
		    tmp25 = c_re(W[19]);
		    tmp27 = c_im(W[19]);
		    tmp29 = (tmp25 * tmp26) + (tmp27 * tmp28);
		    tmp194 = (tmp25 * tmp28) - (tmp27 * tmp26);
	       }
	       {
		    fftw_real tmp32;
		    fftw_real tmp34;
		    fftw_real tmp31;
		    fftw_real tmp33;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp32 = c_re(inout[28 * iostride]);
		    tmp34 = c_im(inout[28 * iostride]);
		    tmp31 = c_re(W[27]);
		    tmp33 = c_im(W[27]);
		    tmp35 = (tmp31 * tmp32) + (tmp33 * tmp34);
		    tmp198 = (tmp31 * tmp34) - (tmp33 * tmp32);
	       }
	       {
		    fftw_real tmp30;
		    fftw_real tmp41;
		    fftw_real tmp197;
		    fftw_real tmp200;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp30 = tmp24 + tmp29;
		    tmp41 = tmp35 + tmp40;
		    tmp42 = tmp30 + tmp41;
		    tmp455 = tmp30 - tmp41;
		    tmp197 = tmp35 - tmp40;
		    tmp200 = tmp198 - tmp199;
		    tmp201 = tmp197 + tmp200;
		    tmp304 = tmp200 - tmp197;
	       }
	       {
		    fftw_real tmp388;
		    fftw_real tmp389;
		    fftw_real tmp192;
		    fftw_real tmp195;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp388 = tmp198 + tmp199;
		    tmp389 = tmp193 + tmp194;
		    tmp390 = tmp388 - tmp389;
		    tmp437 = tmp389 + tmp388;
		    tmp192 = tmp24 - tmp29;
		    tmp195 = tmp193 - tmp194;
		    tmp196 = tmp192 - tmp195;
		    tmp305 = tmp192 + tmp195;
	       }
	  }
	  {
	       fftw_real tmp166;
	       fftw_real tmp261;
	       fftw_real tmp171;
	       fftw_real tmp262;
	       fftw_real tmp260;
	       fftw_real tmp263;
	       fftw_real tmp177;
	       fftw_real tmp266;
	       fftw_real tmp182;
	       fftw_real tmp267;
	       fftw_real tmp265;
	       fftw_real tmp268;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp163;
		    fftw_real tmp165;
		    fftw_real tmp162;
		    fftw_real tmp164;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp163 = c_re(inout[3 * iostride]);
		    tmp165 = c_im(inout[3 * iostride]);
		    tmp162 = c_re(W[2]);
		    tmp164 = c_im(W[2]);
		    tmp166 = (tmp162 * tmp163) + (tmp164 * tmp165);
		    tmp261 = (tmp162 * tmp165) - (tmp164 * tmp163);
	       }
	       {
		    fftw_real tmp168;
		    fftw_real tmp170;
		    fftw_real tmp167;
		    fftw_real tmp169;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp168 = c_re(inout[19 * iostride]);
		    tmp170 = c_im(inout[19 * iostride]);
		    tmp167 = c_re(W[18]);
		    tmp169 = c_im(W[18]);
		    tmp171 = (tmp167 * tmp168) + (tmp169 * tmp170);
		    tmp262 = (tmp167 * tmp170) - (tmp169 * tmp168);
	       }
	       tmp260 = tmp166 - tmp171;
	       tmp263 = tmp261 - tmp262;
	       {
		    fftw_real tmp174;
		    fftw_real tmp176;
		    fftw_real tmp173;
		    fftw_real tmp175;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp174 = c_re(inout[27 * iostride]);
		    tmp176 = c_im(inout[27 * iostride]);
		    tmp173 = c_re(W[26]);
		    tmp175 = c_im(W[26]);
		    tmp177 = (tmp173 * tmp174) + (tmp175 * tmp176);
		    tmp266 = (tmp173 * tmp176) - (tmp175 * tmp174);
	       }
	       {
		    fftw_real tmp179;
		    fftw_real tmp181;
		    fftw_real tmp178;
		    fftw_real tmp180;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp179 = c_re(inout[11 * iostride]);
		    tmp181 = c_im(inout[11 * iostride]);
		    tmp178 = c_re(W[10]);
		    tmp180 = c_im(W[10]);
		    tmp182 = (tmp178 * tmp179) + (tmp180 * tmp181);
		    tmp267 = (tmp178 * tmp181) - (tmp180 * tmp179);
	       }
	       tmp265 = tmp177 - tmp182;
	       tmp268 = tmp266 - tmp267;
	       {
		    fftw_real tmp172;
		    fftw_real tmp183;
		    fftw_real tmp373;
		    fftw_real tmp374;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp172 = tmp166 + tmp171;
		    tmp183 = tmp177 + tmp182;
		    tmp184 = tmp172 + tmp183;
		    tmp401 = tmp172 - tmp183;
		    tmp373 = tmp261 + tmp262;
		    tmp374 = tmp266 + tmp267;
		    tmp375 = tmp373 + tmp374;
		    tmp404 = tmp374 - tmp373;
	       }
	       {
		    fftw_real tmp264;
		    fftw_real tmp269;
		    fftw_real tmp277;
		    fftw_real tmp278;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp264 = tmp260 - tmp263;
		    tmp269 = tmp265 + tmp268;
		    tmp270 = K707106781 * (tmp264 + tmp269);
		    tmp317 = K707106781 * (tmp264 - tmp269);
		    tmp277 = tmp260 + tmp263;
		    tmp278 = tmp268 - tmp265;
		    tmp279 = K707106781 * (tmp277 + tmp278);
		    tmp320 = K707106781 * (tmp278 - tmp277);
	       }
	  }
	  {
	       fftw_real tmp48;
	       fftw_real tmp215;
	       fftw_real tmp64;
	       fftw_real tmp222;
	       fftw_real tmp53;
	       fftw_real tmp216;
	       fftw_real tmp59;
	       fftw_real tmp221;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp45;
		    fftw_real tmp47;
		    fftw_real tmp44;
		    fftw_real tmp46;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp45 = c_re(inout[2 * iostride]);
		    tmp47 = c_im(inout[2 * iostride]);
		    tmp44 = c_re(W[1]);
		    tmp46 = c_im(W[1]);
		    tmp48 = (tmp44 * tmp45) + (tmp46 * tmp47);
		    tmp215 = (tmp44 * tmp47) - (tmp46 * tmp45);
	       }
	       {
		    fftw_real tmp61;
		    fftw_real tmp63;
		    fftw_real tmp60;
		    fftw_real tmp62;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp61 = c_re(inout[26 * iostride]);
		    tmp63 = c_im(inout[26 * iostride]);
		    tmp60 = c_re(W[25]);
		    tmp62 = c_im(W[25]);
		    tmp64 = (tmp60 * tmp61) + (tmp62 * tmp63);
		    tmp222 = (tmp60 * tmp63) - (tmp62 * tmp61);
	       }
	       {
		    fftw_real tmp50;
		    fftw_real tmp52;
		    fftw_real tmp49;
		    fftw_real tmp51;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp50 = c_re(inout[18 * iostride]);
		    tmp52 = c_im(inout[18 * iostride]);
		    tmp49 = c_re(W[17]);
		    tmp51 = c_im(W[17]);
		    tmp53 = (tmp49 * tmp50) + (tmp51 * tmp52);
		    tmp216 = (tmp49 * tmp52) - (tmp51 * tmp50);
	       }
	       {
		    fftw_real tmp56;
		    fftw_real tmp58;
		    fftw_real tmp55;
		    fftw_real tmp57;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp56 = c_re(inout[10 * iostride]);
		    tmp58 = c_im(inout[10 * iostride]);
		    tmp55 = c_re(W[9]);
		    tmp57 = c_im(W[9]);
		    tmp59 = (tmp55 * tmp56) + (tmp57 * tmp58);
		    tmp221 = (tmp55 * tmp58) - (tmp57 * tmp56);
	       }
	       {
		    fftw_real tmp54;
		    fftw_real tmp65;
		    fftw_real tmp220;
		    fftw_real tmp223;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp54 = tmp48 + tmp53;
		    tmp65 = tmp59 + tmp64;
		    tmp66 = tmp54 + tmp65;
		    tmp395 = tmp54 - tmp65;
		    tmp220 = tmp48 - tmp53;
		    tmp223 = tmp221 - tmp222;
		    tmp224 = tmp220 - tmp223;
		    tmp312 = tmp220 + tmp223;
	       }
	       {
		    fftw_real tmp355;
		    fftw_real tmp356;
		    fftw_real tmp217;
		    fftw_real tmp218;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp355 = tmp215 + tmp216;
		    tmp356 = tmp221 + tmp222;
		    tmp357 = tmp355 + tmp356;
		    tmp396 = tmp355 - tmp356;
		    tmp217 = tmp215 - tmp216;
		    tmp218 = tmp59 - tmp64;
		    tmp219 = tmp217 + tmp218;
		    tmp311 = tmp217 - tmp218;
	       }
	  }
	  {
	       fftw_real tmp96;
	       fftw_real tmp245;
	       fftw_real tmp112;
	       fftw_real tmp230;
	       fftw_real tmp101;
	       fftw_real tmp246;
	       fftw_real tmp107;
	       fftw_real tmp229;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp93;
		    fftw_real tmp95;
		    fftw_real tmp92;
		    fftw_real tmp94;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp93 = c_re(inout[iostride]);
		    tmp95 = c_im(inout[iostride]);
		    tmp92 = c_re(W[0]);
		    tmp94 = c_im(W[0]);
		    tmp96 = (tmp92 * tmp93) + (tmp94 * tmp95);
		    tmp245 = (tmp92 * tmp95) - (tmp94 * tmp93);
	       }
	       {
		    fftw_real tmp109;
		    fftw_real tmp111;
		    fftw_real tmp108;
		    fftw_real tmp110;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp109 = c_re(inout[25 * iostride]);
		    tmp111 = c_im(inout[25 * iostride]);
		    tmp108 = c_re(W[24]);
		    tmp110 = c_im(W[24]);
		    tmp112 = (tmp108 * tmp109) + (tmp110 * tmp111);
		    tmp230 = (tmp108 * tmp111) - (tmp110 * tmp109);
	       }
	       {
		    fftw_real tmp98;
		    fftw_real tmp100;
		    fftw_real tmp97;
		    fftw_real tmp99;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp98 = c_re(inout[17 * iostride]);
		    tmp100 = c_im(inout[17 * iostride]);
		    tmp97 = c_re(W[16]);
		    tmp99 = c_im(W[16]);
		    tmp101 = (tmp97 * tmp98) + (tmp99 * tmp100);
		    tmp246 = (tmp97 * tmp100) - (tmp99 * tmp98);
	       }
	       {
		    fftw_real tmp104;
		    fftw_real tmp106;
		    fftw_real tmp103;
		    fftw_real tmp105;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp104 = c_re(inout[9 * iostride]);
		    tmp106 = c_im(inout[9 * iostride]);
		    tmp103 = c_re(W[8]);
		    tmp105 = c_im(W[8]);
		    tmp107 = (tmp103 * tmp104) + (tmp105 * tmp106);
		    tmp229 = (tmp103 * tmp106) - (tmp105 * tmp104);
	       }
	       {
		    fftw_real tmp102;
		    fftw_real tmp113;
		    fftw_real tmp247;
		    fftw_real tmp248;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp102 = tmp96 + tmp101;
		    tmp113 = tmp107 + tmp112;
		    tmp114 = tmp102 + tmp113;
		    tmp410 = tmp102 - tmp113;
		    tmp247 = tmp245 - tmp246;
		    tmp248 = tmp107 - tmp112;
		    tmp249 = tmp247 + tmp248;
		    tmp323 = tmp247 - tmp248;
	       }
	       {
		    fftw_real tmp361;
		    fftw_real tmp362;
		    fftw_real tmp228;
		    fftw_real tmp231;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp361 = tmp245 + tmp246;
		    tmp362 = tmp229 + tmp230;
		    tmp363 = tmp361 + tmp362;
		    tmp407 = tmp361 - tmp362;
		    tmp228 = tmp96 - tmp101;
		    tmp231 = tmp229 - tmp230;
		    tmp232 = tmp228 - tmp231;
		    tmp326 = tmp228 + tmp231;
	       }
	  }
	  {
	       fftw_real tmp71;
	       fftw_real tmp204;
	       fftw_real tmp87;
	       fftw_real tmp211;
	       fftw_real tmp76;
	       fftw_real tmp205;
	       fftw_real tmp82;
	       fftw_real tmp210;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp68;
		    fftw_real tmp70;
		    fftw_real tmp67;
		    fftw_real tmp69;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp68 = c_re(inout[30 * iostride]);
		    tmp70 = c_im(inout[30 * iostride]);
		    tmp67 = c_re(W[29]);
		    tmp69 = c_im(W[29]);
		    tmp71 = (tmp67 * tmp68) + (tmp69 * tmp70);
		    tmp204 = (tmp67 * tmp70) - (tmp69 * tmp68);
	       }
	       {
		    fftw_real tmp84;
		    fftw_real tmp86;
		    fftw_real tmp83;
		    fftw_real tmp85;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp84 = c_re(inout[22 * iostride]);
		    tmp86 = c_im(inout[22 * iostride]);
		    tmp83 = c_re(W[21]);
		    tmp85 = c_im(W[21]);
		    tmp87 = (tmp83 * tmp84) + (tmp85 * tmp86);
		    tmp211 = (tmp83 * tmp86) - (tmp85 * tmp84);
	       }
	       {
		    fftw_real tmp73;
		    fftw_real tmp75;
		    fftw_real tmp72;
		    fftw_real tmp74;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp73 = c_re(inout[14 * iostride]);
		    tmp75 = c_im(inout[14 * iostride]);
		    tmp72 = c_re(W[13]);
		    tmp74 = c_im(W[13]);
		    tmp76 = (tmp72 * tmp73) + (tmp74 * tmp75);
		    tmp205 = (tmp72 * tmp75) - (tmp74 * tmp73);
	       }
	       {
		    fftw_real tmp79;
		    fftw_real tmp81;
		    fftw_real tmp78;
		    fftw_real tmp80;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp79 = c_re(inout[6 * iostride]);
		    tmp81 = c_im(inout[6 * iostride]);
		    tmp78 = c_re(W[5]);
		    tmp80 = c_im(W[5]);
		    tmp82 = (tmp78 * tmp79) + (tmp80 * tmp81);
		    tmp210 = (tmp78 * tmp81) - (tmp80 * tmp79);
	       }
	       {
		    fftw_real tmp77;
		    fftw_real tmp88;
		    fftw_real tmp209;
		    fftw_real tmp212;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp77 = tmp71 + tmp76;
		    tmp88 = tmp82 + tmp87;
		    tmp89 = tmp77 + tmp88;
		    tmp393 = tmp77 - tmp88;
		    tmp209 = tmp71 - tmp76;
		    tmp212 = tmp210 - tmp211;
		    tmp213 = tmp209 - tmp212;
		    tmp309 = tmp209 + tmp212;
	       }
	       {
		    fftw_real tmp352;
		    fftw_real tmp353;
		    fftw_real tmp206;
		    fftw_real tmp207;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp352 = tmp204 + tmp205;
		    tmp353 = tmp210 + tmp211;
		    tmp354 = tmp352 + tmp353;
		    tmp392 = tmp352 - tmp353;
		    tmp206 = tmp204 - tmp205;
		    tmp207 = tmp82 - tmp87;
		    tmp208 = tmp206 + tmp207;
		    tmp308 = tmp206 - tmp207;
	       }
	  }
	  {
	       fftw_real tmp119;
	       fftw_real tmp234;
	       fftw_real tmp124;
	       fftw_real tmp235;
	       fftw_real tmp233;
	       fftw_real tmp236;
	       fftw_real tmp130;
	       fftw_real tmp239;
	       fftw_real tmp135;
	       fftw_real tmp240;
	       fftw_real tmp238;
	       fftw_real tmp241;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp116;
		    fftw_real tmp118;
		    fftw_real tmp115;
		    fftw_real tmp117;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp116 = c_re(inout[5 * iostride]);
		    tmp118 = c_im(inout[5 * iostride]);
		    tmp115 = c_re(W[4]);
		    tmp117 = c_im(W[4]);
		    tmp119 = (tmp115 * tmp116) + (tmp117 * tmp118);
		    tmp234 = (tmp115 * tmp118) - (tmp117 * tmp116);
	       }
	       {
		    fftw_real tmp121;
		    fftw_real tmp123;
		    fftw_real tmp120;
		    fftw_real tmp122;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp121 = c_re(inout[21 * iostride]);
		    tmp123 = c_im(inout[21 * iostride]);
		    tmp120 = c_re(W[20]);
		    tmp122 = c_im(W[20]);
		    tmp124 = (tmp120 * tmp121) + (tmp122 * tmp123);
		    tmp235 = (tmp120 * tmp123) - (tmp122 * tmp121);
	       }
	       tmp233 = tmp119 - tmp124;
	       tmp236 = tmp234 - tmp235;
	       {
		    fftw_real tmp127;
		    fftw_real tmp129;
		    fftw_real tmp126;
		    fftw_real tmp128;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp127 = c_re(inout[29 * iostride]);
		    tmp129 = c_im(inout[29 * iostride]);
		    tmp126 = c_re(W[28]);
		    tmp128 = c_im(W[28]);
		    tmp130 = (tmp126 * tmp127) + (tmp128 * tmp129);
		    tmp239 = (tmp126 * tmp129) - (tmp128 * tmp127);
	       }
	       {
		    fftw_real tmp132;
		    fftw_real tmp134;
		    fftw_real tmp131;
		    fftw_real tmp133;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp132 = c_re(inout[13 * iostride]);
		    tmp134 = c_im(inout[13 * iostride]);
		    tmp131 = c_re(W[12]);
		    tmp133 = c_im(W[12]);
		    tmp135 = (tmp131 * tmp132) + (tmp133 * tmp134);
		    tmp240 = (tmp131 * tmp134) - (tmp133 * tmp132);
	       }
	       tmp238 = tmp130 - tmp135;
	       tmp241 = tmp239 - tmp240;
	       {
		    fftw_real tmp125;
		    fftw_real tmp136;
		    fftw_real tmp364;
		    fftw_real tmp365;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp125 = tmp119 + tmp124;
		    tmp136 = tmp130 + tmp135;
		    tmp137 = tmp125 + tmp136;
		    tmp408 = tmp125 - tmp136;
		    tmp364 = tmp234 + tmp235;
		    tmp365 = tmp239 + tmp240;
		    tmp366 = tmp364 + tmp365;
		    tmp411 = tmp365 - tmp364;
	       }
	       {
		    fftw_real tmp237;
		    fftw_real tmp242;
		    fftw_real tmp250;
		    fftw_real tmp251;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp237 = tmp233 - tmp236;
		    tmp242 = tmp238 + tmp241;
		    tmp243 = K707106781 * (tmp237 + tmp242);
		    tmp324 = K707106781 * (tmp237 - tmp242);
		    tmp250 = tmp233 + tmp236;
		    tmp251 = tmp241 - tmp238;
		    tmp252 = K707106781 * (tmp250 + tmp251);
		    tmp327 = K707106781 * (tmp251 - tmp250);
	       }
	  }
	  {
	       fftw_real tmp91;
	       fftw_real tmp383;
	       fftw_real tmp444;
	       fftw_real tmp446;
	       fftw_real tmp186;
	       fftw_real tmp445;
	       fftw_real tmp386;
	       fftw_real tmp435;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp43;
		    fftw_real tmp90;
		    fftw_real tmp436;
		    fftw_real tmp443;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp43 = tmp19 + tmp42;
		    tmp90 = tmp66 + tmp89;
		    tmp91 = tmp43 + tmp90;
		    tmp383 = tmp43 - tmp90;
		    tmp436 = tmp357 + tmp354;
		    tmp443 = tmp437 + tmp442;
		    tmp444 = tmp436 + tmp443;
		    tmp446 = tmp443 - tmp436;
	       }
	       {
		    fftw_real tmp138;
		    fftw_real tmp185;
		    fftw_real tmp384;
		    fftw_real tmp385;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp138 = tmp114 + tmp137;
		    tmp185 = tmp161 + tmp184;
		    tmp186 = tmp138 + tmp185;
		    tmp445 = tmp138 - tmp185;
		    tmp384 = tmp372 + tmp375;
		    tmp385 = tmp363 + tmp366;
		    tmp386 = tmp384 - tmp385;
		    tmp435 = tmp385 + tmp384;
	       }
	       c_re(inout[16 * iostride]) = tmp91 - tmp186;
	       c_re(inout[0]) = tmp91 + tmp186;
	       c_re(inout[24 * iostride]) = tmp383 - tmp386;
	       c_re(inout[8 * iostride]) = tmp383 + tmp386;
	       c_im(inout[0]) = tmp435 + tmp444;
	       c_im(inout[16 * iostride]) = tmp444 - tmp435;
	       c_im(inout[8 * iostride]) = tmp445 + tmp446;
	       c_im(inout[24 * iostride]) = tmp446 - tmp445;
	  }
	  {
	       fftw_real tmp359;
	       fftw_real tmp379;
	       fftw_real tmp450;
	       fftw_real tmp452;
	       fftw_real tmp368;
	       fftw_real tmp381;
	       fftw_real tmp377;
	       fftw_real tmp380;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp351;
		    fftw_real tmp358;
		    fftw_real tmp448;
		    fftw_real tmp449;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp351 = tmp19 - tmp42;
		    tmp358 = tmp354 - tmp357;
		    tmp359 = tmp351 + tmp358;
		    tmp379 = tmp351 - tmp358;
		    tmp448 = tmp66 - tmp89;
		    tmp449 = tmp442 - tmp437;
		    tmp450 = tmp448 + tmp449;
		    tmp452 = tmp449 - tmp448;
	       }
	       {
		    fftw_real tmp360;
		    fftw_real tmp367;
		    fftw_real tmp369;
		    fftw_real tmp376;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp360 = tmp114 - tmp137;
		    tmp367 = tmp363 - tmp366;
		    tmp368 = tmp360 - tmp367;
		    tmp381 = tmp360 + tmp367;
		    tmp369 = tmp161 - tmp184;
		    tmp376 = tmp372 - tmp375;
		    tmp377 = tmp369 + tmp376;
		    tmp380 = tmp376 - tmp369;
	       }
	       {
		    fftw_real tmp378;
		    fftw_real tmp451;
		    fftw_real tmp382;
		    fftw_real tmp447;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp378 = K707106781 * (tmp368 + tmp377);
		    c_re(inout[20 * iostride]) = tmp359 - tmp378;
		    c_re(inout[4 * iostride]) = tmp359 + tmp378;
		    tmp451 = K707106781 * (tmp368 - tmp377);
		    c_im(inout[12 * iostride]) = tmp451 + tmp452;
		    c_im(inout[28 * iostride]) = tmp452 - tmp451;
		    tmp382 = K707106781 * (tmp380 - tmp381);
		    c_re(inout[28 * iostride]) = tmp379 - tmp382;
		    c_re(inout[12 * iostride]) = tmp379 + tmp382;
		    tmp447 = K707106781 * (tmp381 + tmp380);
		    c_im(inout[4 * iostride]) = tmp447 + tmp450;
		    c_im(inout[20 * iostride]) = tmp450 - tmp447;
	       }
	  }
	  {
	       fftw_real tmp391;
	       fftw_real tmp419;
	       fftw_real tmp398;
	       fftw_real tmp454;
	       fftw_real tmp422;
	       fftw_real tmp462;
	       fftw_real tmp406;
	       fftw_real tmp417;
	       fftw_real tmp457;
	       fftw_real tmp463;
	       fftw_real tmp426;
	       fftw_real tmp433;
	       fftw_real tmp413;
	       fftw_real tmp416;
	       fftw_real tmp429;
	       fftw_real tmp432;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp394;
		    fftw_real tmp397;
		    fftw_real tmp424;
		    fftw_real tmp425;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp391 = tmp387 - tmp390;
		    tmp419 = tmp387 + tmp390;
		    tmp394 = tmp392 - tmp393;
		    tmp397 = tmp395 + tmp396;
		    tmp398 = K707106781 * (tmp394 - tmp397);
		    tmp454 = K707106781 * (tmp397 + tmp394);
		    {
			 fftw_real tmp420;
			 fftw_real tmp421;
			 fftw_real tmp402;
			 fftw_real tmp405;
			 ASSERT_ALIGNED_DOUBLE();
			 tmp420 = tmp395 - tmp396;
			 tmp421 = tmp393 + tmp392;
			 tmp422 = K707106781 * (tmp420 + tmp421);
			 tmp462 = K707106781 * (tmp420 - tmp421);
			 tmp402 = tmp400 - tmp401;
			 tmp405 = tmp403 - tmp404;
			 tmp406 = (K382683432 * tmp402) - (K923879532 * tmp405);
			 tmp417 = (K923879532 * tmp402) + (K382683432 * tmp405);
		    }
		    tmp457 = tmp455 + tmp456;
		    tmp463 = tmp456 - tmp455;
		    tmp424 = tmp400 + tmp401;
		    tmp425 = tmp403 + tmp404;
		    tmp426 = (K923879532 * tmp424) - (K382683432 * tmp425);
		    tmp433 = (K382683432 * tmp424) + (K923879532 * tmp425);
		    {
			 fftw_real tmp409;
			 fftw_real tmp412;
			 fftw_real tmp427;
			 fftw_real tmp428;
			 ASSERT_ALIGNED_DOUBLE();
			 tmp409 = tmp407 - tmp408;
			 tmp412 = tmp410 - tmp411;
			 tmp413 = (K382683432 * tmp409) + (K923879532 * tmp412);
			 tmp416 = (K382683432 * tmp412) - (K923879532 * tmp409);
			 tmp427 = tmp407 + tmp408;
			 tmp428 = tmp410 + tmp411;
			 tmp429 = (K923879532 * tmp427) + (K382683432 * tmp428);
			 tmp432 = (K923879532 * tmp428) - (K382683432 * tmp427);
		    }
	       }
	       {
		    fftw_real tmp399;
		    fftw_real tmp414;
		    fftw_real tmp415;
		    fftw_real tmp418;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp399 = tmp391 - tmp398;
		    tmp414 = tmp406 - tmp413;
		    c_re(inout[30 * iostride]) = tmp399 - tmp414;
		    c_re(inout[14 * iostride]) = tmp399 + tmp414;
		    tmp415 = tmp391 + tmp398;
		    tmp418 = tmp416 + tmp417;
		    c_re(inout[22 * iostride]) = tmp415 - tmp418;
		    c_re(inout[6 * iostride]) = tmp415 + tmp418;
	       }
	       {
		    fftw_real tmp465;
		    fftw_real tmp466;
		    fftw_real tmp461;
		    fftw_real tmp464;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp465 = tmp416 - tmp417;
		    tmp466 = tmp463 - tmp462;
		    c_im(inout[14 * iostride]) = tmp465 + tmp466;
		    c_im(inout[30 * iostride]) = tmp466 - tmp465;
		    tmp461 = tmp413 + tmp406;
		    tmp464 = tmp462 + tmp463;
		    c_im(inout[6 * iostride]) = tmp461 + tmp464;
		    c_im(inout[22 * iostride]) = tmp464 - tmp461;
	       }
	       {
		    fftw_real tmp423;
		    fftw_real tmp430;
		    fftw_real tmp431;
		    fftw_real tmp434;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp423 = tmp419 - tmp422;
		    tmp430 = tmp426 - tmp429;
		    c_re(inout[26 * iostride]) = tmp423 - tmp430;
		    c_re(inout[10 * iostride]) = tmp423 + tmp430;
		    tmp431 = tmp419 + tmp422;
		    tmp434 = tmp432 + tmp433;
		    c_re(inout[18 * iostride]) = tmp431 - tmp434;
		    c_re(inout[2 * iostride]) = tmp431 + tmp434;
	       }
	       {
		    fftw_real tmp459;
		    fftw_real tmp460;
		    fftw_real tmp453;
		    fftw_real tmp458;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp459 = tmp432 - tmp433;
		    tmp460 = tmp457 - tmp454;
		    c_im(inout[10 * iostride]) = tmp459 + tmp460;
		    c_im(inout[26 * iostride]) = tmp460 - tmp459;
		    tmp453 = tmp429 + tmp426;
		    tmp458 = tmp454 + tmp457;
		    c_im(inout[2 * iostride]) = tmp453 + tmp458;
		    c_im(inout[18 * iostride]) = tmp458 - tmp453;
	       }
	  }
	  {
	       fftw_real tmp307;
	       fftw_real tmp335;
	       fftw_real tmp338;
	       fftw_real tmp492;
	       fftw_real tmp487;
	       fftw_real tmp493;
	       fftw_real tmp314;
	       fftw_real tmp484;
	       fftw_real tmp322;
	       fftw_real tmp333;
	       fftw_real tmp342;
	       fftw_real tmp349;
	       fftw_real tmp329;
	       fftw_real tmp332;
	       fftw_real tmp345;
	       fftw_real tmp348;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp306;
		    fftw_real tmp336;
		    fftw_real tmp337;
		    fftw_real tmp485;
		    fftw_real tmp310;
		    fftw_real tmp313;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp306 = K707106781 * (tmp304 - tmp305);
		    tmp307 = tmp303 - tmp306;
		    tmp335 = tmp303 + tmp306;
		    tmp336 = (K382683432 * tmp312) - (K923879532 * tmp311);
		    tmp337 = (K923879532 * tmp308) + (K382683432 * tmp309);
		    tmp338 = tmp336 + tmp337;
		    tmp492 = tmp336 - tmp337;
		    tmp485 = K707106781 * (tmp196 - tmp201);
		    tmp487 = tmp485 + tmp486;
		    tmp493 = tmp486 - tmp485;
		    tmp310 = (K382683432 * tmp308) - (K923879532 * tmp309);
		    tmp313 = (K382683432 * tmp311) + (K923879532 * tmp312);
		    tmp314 = tmp310 - tmp313;
		    tmp484 = tmp313 + tmp310;
	       }
	       {
		    fftw_real tmp318;
		    fftw_real tmp321;
		    fftw_real tmp340;
		    fftw_real tmp341;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp318 = tmp316 - tmp317;
		    tmp321 = tmp319 - tmp320;
		    tmp322 = (K195090322 * tmp318) - (K980785280 * tmp321);
		    tmp333 = (K980785280 * tmp318) + (K195090322 * tmp321);
		    tmp340 = tmp316 + tmp317;
		    tmp341 = tmp319 + tmp320;
		    tmp342 = (K831469612 * tmp340) - (K555570233 * tmp341);
		    tmp349 = (K555570233 * tmp340) + (K831469612 * tmp341);
	       }
	       {
		    fftw_real tmp325;
		    fftw_real tmp328;
		    fftw_real tmp343;
		    fftw_real tmp344;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp325 = tmp323 - tmp324;
		    tmp328 = tmp326 - tmp327;
		    tmp329 = (K195090322 * tmp325) + (K980785280 * tmp328);
		    tmp332 = (K195090322 * tmp328) - (K980785280 * tmp325);
		    tmp343 = tmp323 + tmp324;
		    tmp344 = tmp326 + tmp327;
		    tmp345 = (K831469612 * tmp343) + (K555570233 * tmp344);
		    tmp348 = (K831469612 * tmp344) - (K555570233 * tmp343);
	       }
	       {
		    fftw_real tmp315;
		    fftw_real tmp330;
		    fftw_real tmp331;
		    fftw_real tmp334;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp315 = tmp307 - tmp314;
		    tmp330 = tmp322 - tmp329;
		    c_re(inout[31 * iostride]) = tmp315 - tmp330;
		    c_re(inout[15 * iostride]) = tmp315 + tmp330;
		    tmp331 = tmp307 + tmp314;
		    tmp334 = tmp332 + tmp333;
		    c_re(inout[23 * iostride]) = tmp331 - tmp334;
		    c_re(inout[7 * iostride]) = tmp331 + tmp334;
	       }
	       {
		    fftw_real tmp495;
		    fftw_real tmp496;
		    fftw_real tmp491;
		    fftw_real tmp494;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp495 = tmp332 - tmp333;
		    tmp496 = tmp493 - tmp492;
		    c_im(inout[15 * iostride]) = tmp495 + tmp496;
		    c_im(inout[31 * iostride]) = tmp496 - tmp495;
		    tmp491 = tmp329 + tmp322;
		    tmp494 = tmp492 + tmp493;
		    c_im(inout[7 * iostride]) = tmp491 + tmp494;
		    c_im(inout[23 * iostride]) = tmp494 - tmp491;
	       }
	       {
		    fftw_real tmp339;
		    fftw_real tmp346;
		    fftw_real tmp347;
		    fftw_real tmp350;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp339 = tmp335 - tmp338;
		    tmp346 = tmp342 - tmp345;
		    c_re(inout[27 * iostride]) = tmp339 - tmp346;
		    c_re(inout[11 * iostride]) = tmp339 + tmp346;
		    tmp347 = tmp335 + tmp338;
		    tmp350 = tmp348 + tmp349;
		    c_re(inout[19 * iostride]) = tmp347 - tmp350;
		    c_re(inout[3 * iostride]) = tmp347 + tmp350;
	       }
	       {
		    fftw_real tmp489;
		    fftw_real tmp490;
		    fftw_real tmp483;
		    fftw_real tmp488;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp489 = tmp348 - tmp349;
		    tmp490 = tmp487 - tmp484;
		    c_im(inout[11 * iostride]) = tmp489 + tmp490;
		    c_im(inout[27 * iostride]) = tmp490 - tmp489;
		    tmp483 = tmp345 + tmp342;
		    tmp488 = tmp484 + tmp487;
		    c_im(inout[3 * iostride]) = tmp483 + tmp488;
		    c_im(inout[19 * iostride]) = tmp488 - tmp483;
	       }
	  }
	  {
	       fftw_real tmp203;
	       fftw_real tmp287;
	       fftw_real tmp290;
	       fftw_real tmp478;
	       fftw_real tmp473;
	       fftw_real tmp479;
	       fftw_real tmp226;
	       fftw_real tmp468;
	       fftw_real tmp254;
	       fftw_real tmp285;
	       fftw_real tmp294;
	       fftw_real tmp301;
	       fftw_real tmp281;
	       fftw_real tmp284;
	       fftw_real tmp297;
	       fftw_real tmp300;
	       ASSERT_ALIGNED_DOUBLE();
	       {
		    fftw_real tmp202;
		    fftw_real tmp288;
		    fftw_real tmp289;
		    fftw_real tmp469;
		    fftw_real tmp214;
		    fftw_real tmp225;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp202 = K707106781 * (tmp196 + tmp201);
		    tmp203 = tmp191 - tmp202;
		    tmp287 = tmp191 + tmp202;
		    tmp288 = (K923879532 * tmp224) - (K382683432 * tmp219);
		    tmp289 = (K382683432 * tmp208) + (K923879532 * tmp213);
		    tmp290 = tmp288 + tmp289;
		    tmp478 = tmp288 - tmp289;
		    tmp469 = K707106781 * (tmp305 + tmp304);
		    tmp473 = tmp469 + tmp472;
		    tmp479 = tmp472 - tmp469;
		    tmp214 = (K923879532 * tmp208) - (K382683432 * tmp213);
		    tmp225 = (K923879532 * tmp219) + (K382683432 * tmp224);
		    tmp226 = tmp214 - tmp225;
		    tmp468 = tmp225 + tmp214;
	       }
	       {
		    fftw_real tmp244;
		    fftw_real tmp253;
		    fftw_real tmp292;
		    fftw_real tmp293;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp244 = tmp232 - tmp243;
		    tmp253 = tmp249 - tmp252;
		    tmp254 = (K555570233 * tmp244) - (K831469612 * tmp253);
		    tmp285 = (K831469612 * tmp244) + (K555570233 * tmp253);
		    tmp292 = tmp232 + tmp243;
		    tmp293 = tmp249 + tmp252;
		    tmp294 = (K980785280 * tmp292) - (K195090322 * tmp293);
		    tmp301 = (K195090322 * tmp292) + (K980785280 * tmp293);
	       }
	       {
		    fftw_real tmp271;
		    fftw_real tmp280;
		    fftw_real tmp295;
		    fftw_real tmp296;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp271 = tmp259 - tmp270;
		    tmp280 = tmp276 - tmp279;
		    tmp281 = (K555570233 * tmp271) + (K831469612 * tmp280);
		    tmp284 = (K555570233 * tmp280) - (K831469612 * tmp271);
		    tmp295 = tmp259 + tmp270;
		    tmp296 = tmp276 + tmp279;
		    tmp297 = (K980785280 * tmp295) + (K195090322 * tmp296);
		    tmp300 = (K980785280 * tmp296) - (K195090322 * tmp295);
	       }
	       {
		    fftw_real tmp227;
		    fftw_real tmp282;
		    fftw_real tmp283;
		    fftw_real tmp286;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp227 = tmp203 + tmp226;
		    tmp282 = tmp254 + tmp281;
		    c_re(inout[21 * iostride]) = tmp227 - tmp282;
		    c_re(inout[5 * iostride]) = tmp227 + tmp282;
		    tmp283 = tmp203 - tmp226;
		    tmp286 = tmp284 - tmp285;
		    c_re(inout[29 * iostride]) = tmp283 - tmp286;
		    c_re(inout[13 * iostride]) = tmp283 + tmp286;
	       }
	       {
		    fftw_real tmp477;
		    fftw_real tmp480;
		    fftw_real tmp481;
		    fftw_real tmp482;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp477 = tmp285 + tmp284;
		    tmp480 = tmp478 + tmp479;
		    c_im(inout[5 * iostride]) = tmp477 + tmp480;
		    c_im(inout[21 * iostride]) = tmp480 - tmp477;
		    tmp481 = tmp254 - tmp281;
		    tmp482 = tmp479 - tmp478;
		    c_im(inout[13 * iostride]) = tmp481 + tmp482;
		    c_im(inout[29 * iostride]) = tmp482 - tmp481;
	       }
	       {
		    fftw_real tmp291;
		    fftw_real tmp298;
		    fftw_real tmp299;
		    fftw_real tmp302;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp291 = tmp287 + tmp290;
		    tmp298 = tmp294 + tmp297;
		    c_re(inout[17 * iostride]) = tmp291 - tmp298;
		    c_re(inout[iostride]) = tmp291 + tmp298;
		    tmp299 = tmp287 - tmp290;
		    tmp302 = tmp300 - tmp301;
		    c_re(inout[25 * iostride]) = tmp299 - tmp302;
		    c_re(inout[9 * iostride]) = tmp299 + tmp302;
	       }
	       {
		    fftw_real tmp467;
		    fftw_real tmp474;
		    fftw_real tmp475;
		    fftw_real tmp476;
		    ASSERT_ALIGNED_DOUBLE();
		    tmp467 = tmp301 + tmp300;
		    tmp474 = tmp468 + tmp473;
		    c_im(inout[iostride]) = tmp467 + tmp474;
		    c_im(inout[17 * iostride]) = tmp474 - tmp467;
		    tmp475 = tmp294 - tmp297;
		    tmp476 = tmp473 - tmp468;
		    c_im(inout[9 * iostride]) = tmp475 + tmp476;
		    c_im(inout[25 * iostride]) = tmp476 - tmp475;
	       }
	  }
     }
}

static const int twiddle_order[] =
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
fftw_codelet_desc fftwi_twiddle_32_desc =
{
     "fftwi_twiddle_32",
     (void (*)()) fftwi_twiddle_32,
     32,
     FFTW_BACKWARD,
     FFTW_TWIDDLE,
     520,
     31,
     twiddle_order,
};


/**
 * @file utilities.h
 * @author Matthew Nicely (mnicely@nvidia.com)
 * @date 2020-01-06
 * @version 1.0
 * @brief Contains additional functions used throughout
 * the application.
 *
 * @copyright Copyright (c) 2020
 *
 * @license This project is released under the GNU Public License
 *
 * @note * Target Processor: Intel x86
 * @n * Target Compiler: GCC 7.4.0
 * @n * NVCC Compiler: CUDA Toolkit 10.0 or later
 */
#ifndef UTILITIES_H_
#define UTILITIES_H_

#include <algorithm>  // std::remove_if, std::nth_element
#include <cassert>    // assert
#include <cblas.h>    //cblas_sgemm
#include <cmath>      // std::sqrt,
#include <fstream>    // std::ofstream
#include <iterator>   // std::vector<>::const_iterator
#include <lapacke.h>  // LAPACKE_sgetrf, LAPACKE_sgetri
#include <map>        // std::map
#include <numeric>    // std::accumulate, std::inner_product
#include <random>     // std::default_random_engine, std::random_device, std::uniform_real_distribution
#include <stdexcept>  // std::runtime_error
#include <string>     // std::string
#include <vector>     // std::vector

// *************** FOR ERROR CHECKING *******************
#ifndef CUDA_RT_CALL
#define CUDA_RT_CALL( call )                                                                                \
    {                                                                                \
        auto status = static_cast<cudaError_t>( call );                                                                \
        if ( status != cudaSuccess )                                                                                \
            fprintf( stderr,                                                                                \
                     "ERROR: CUDA RT call \"%s\" in line %d of file %s failed "                                        \
                     "with "                                                                                \
                     "%s (%d).\n",                                                                                \
                     #call,                                                                                \
                     __LINE__,                                                                                \
                     __FILE__,                                                                                \
                     cudaGetErrorString( status ),                                                                     \
                     status );                                                                                \
    }
#endif  // CUDA_RT_CALL
// *************** FOR ERROR CHECKING *******************

/**
 * @brief Contains additional functions used throughout application
 */
namespace utility {

constexpr int kSysDim { 4 };  /**< Number of states in system model */
constexpr int kMeasDim { 2 }; /**< Number of states in measurement model */

/**
 * @enum Processor
 * @brief Enumeration of processor types
 */
enum class Processor : bool { kCpu, kGpu };

using ProcessorMap = std::map<Processor, std::string>;

static const ProcessorMap processor_map = { { Processor::kCpu, "CPU" }, { Processor::kGpu, "GPU" } };

/**
 * @enum Timing
 * @brief Enumeration of timing calculations
 */
enum class Timing : int { kMedian, kMean, kStdDev, kCount };

using TimingMap = std::map<Timing, std::string>;

static const TimingMap timing_map = { { Timing::kMedian, "Median" },
                                      { Timing::kMean, "Mean" },
                                      { Timing::kStdDev, "StdDev" } };

/**
 * @enum Filter
 * @brief Enumeration of possible particle filters
 */
enum class Filter : int { kBootstrap, kCount };

using FilterMap = std::map<Filter, std::string>;

static const FilterMap filters_map = { { Filter::kBootstrap, "Bootstrap" } };

/**
 * @enum Method
 * @brief Enumeration of possible resampling method
 */
enum class Method : int { kSystematic, kStratified, kMetropolisC2, kCount };

using MethodMap = std::map<Method, std::string>;

static const MethodMap method_map = { { Method::kSystematic, "Systematic" },
                                      { Method::kStratified, "Stratified" },
                                      { Method::kMetropolisC2, "MetropolisC2" } };

/**
 * @struct FilterInfo
 * @brief Structure to store user-specified data and send to filter classes
 */
typedef struct _filterInfo {
    int samples;     /**< Quantity of samples specified from input file */
    int particles;   /**< Quantity of particles specified from input file */
    int num_mcs;     /**< Quantity of Monte Carlos specified from input file */
    int resampling;  /**< Systmatic = 0, Stratified = 1, Metropolis = 2 */
    int filter_type; /**< Type of filter: Bootstrap = 0 */
} FilterInfo;

/**
 * @struct Matrix
 * @brief Structure to store matrix and information
 *
 * @param[in] row Stores quantity of rows
 * @param[in] col Stores quantity of columns
 * @param[in] val Store matrix data in a vector
 */
typedef struct _matrix {

    int                row;
    int                col;
    std::vector<float> val;

    _matrix( const _matrix &a ) : row( a.row ), col( a.col ), val( a.val ) {};

    _matrix( int const &row, int const &col, std::initializer_list<float> const &list ) :
        row( row ), col( col ), val( list ) {}

    _matrix( int const &row, int const &col ) : row( row ), col( col ) {
        val.resize( row * col, 0.0f );
    }

    _matrix( int const &row, int const &col, std::vector<float> const val ) : row( row ), col( col ), val( val ) {}
} Matrix;

/**
 * @brief Template function to compute square root of a matrix using floats
 *
 * @see http://www.netlib.org/lapack/lapacke.html
 * @see
 * http://www.netlib.org/lapack/explore-html/d7/d75/lapacke__ssyevr_8c_source.html
 * @see http://www.openblas.net/
 * @see http://www.seehuhn.de/pages/matrixfn
 *
 * @param[in,out] matrix_a Matrix to process
 */
inline void MatrixSqrt( utility::Matrix &matrix_a ) {

    int    n { matrix_a.row };
    float *a { matrix_a.val.data( ) };

    /* allocate space for the output parameters and workspace arrays */
    int   m {};
    float abstol { -1.0f };

    std::vector<float> w( n, 0.0f );
    std::vector<float> z( n * n, 0.0f );
    std::vector<int>   suppz( 2 * n, 0 );

    /* get the eigenvalues and eigenvectors */
    int info = LAPACKE_ssyevr(
        LAPACK_ROW_MAJOR, 'V', 'A', 'L', n, a, n, 0, 0, 0, 0, abstol, &m, w.data( ), z.data( ), n, suppz.data( ) );

    if ( info != 0 ) {
        throw std::runtime_error( "Issue with LAPACKE_ssyevr.\n" );
    }

    /* allocate and Initialize a new matrix B=Z*D */
    std::vector<float> b( n * n, 0.0f );
    for ( int j = 0; j < n; ++j ) {
        float lambda = std::sqrt( w[j] );
        for ( int i = 0; i < n; ++i ) {
            b[i * n + j] = z[i * n + j] * lambda;
        }
    }

    float alpha { 1.0f };
    float beta { 0.0f };

    /* calculate the square root A=B*Z^T */
    cblas_sgemm( CblasRowMajor, CblasNoTrans, CblasTrans, n, n, n, alpha, b.data( ), n, z.data( ), n, beta, a, n );
}

/**
 * @brief Matrix-Matrix multiplication function using floats
 *
 * @see http://www.openblas.net/
 *
 * @param[out] matrix_c Structure to store A * B
 * @param[in] matrix_a Structure reference to matrix A
 * @param[in] matrix_b Structure reference to matrix B
 */

inline void MatrixMult( utility::Matrix &matrix_c, utility::Matrix const &matrix_a, utility::Matrix const &matrix_b ) {

    assert( matrix_a.col == matrix_b.row );

    float const *a { matrix_a.val.data( ) };
    float const *b { matrix_b.val.data( ) };
    float *      c { matrix_c.val.data( ) };

    float alpha { 1.0f };
    float beta { 0.0f };

    cblas_sgemm( CblasRowMajor,
                 CblasNoTrans,
                 CblasNoTrans,
                 matrix_a.row,
                 matrix_b.col,
                 matrix_b.row,
                 alpha,
                 a,
                 matrix_b.row,
                 b,
                 matrix_b.col,
                 beta,
                 c,
                 matrix_b.col );
}

/**
 * @brief Matrix-Matrix-Matrix multiplication function using floats
 *
 * @see http://www.openblas.net/
 *
 * @param[out] matrix_d Structure to store A * B * C
 * @param[in] matrix_a Structure reference to matrix A
 * @param[in] matrix_b Structure reference to matrix B
 * @param[in] matrix_c Structure reference to matrix C
 */
inline void MatrixMult( utility::Matrix &      matrix_d,
                        utility::Matrix const &matrix_a,
                        utility::Matrix const &matrix_b,
                        utility::Matrix const &matrix_c ) {

    assert( matrix_a.col == matrix_b.row );
    assert( matrix_b.col == matrix_c.row );

    float const *a { matrix_a.val.data( ) };
    float const *b { matrix_b.val.data( ) };
    float const *c { matrix_c.val.data( ) };
    float *      d { matrix_d.val.data( ) };

    std::vector<float> temp_storage( matrix_a.row * matrix_b.col, 0.0f );
    float *            ab { temp_storage.data( ) };  // Temp store for A*B

    float alpha { 1.0f };
    float beta { 0.0f };

    cblas_sgemm( CblasRowMajor,
                 CblasNoTrans,
                 CblasNoTrans,
                 matrix_a.row,
                 matrix_b.col,
                 matrix_b.row,
                 alpha,
                 a,
                 matrix_b.row,
                 b,
                 matrix_b.col,
                 beta,
                 ab,
                 matrix_b.col );

    cblas_sgemm( CblasRowMajor,
                 CblasNoTrans,
                 CblasNoTrans,
                 matrix_a.row,
                 matrix_c.col,
                 matrix_c.row,
                 alpha,
                 ab,
                 matrix_c.row,
                 c,
                 matrix_c.col,
                 beta,
                 d,
                 matrix_c.col );
}

/**
 * @brief Matrix-Vector multiplication function using floats
 *
 * @see http://www.openblas.net/
 *
 * @param[out] vector_c Vector to store A * B
 * @param[in] matrix_a Structure reference to matrix A
 * @param[in] vector_b Vector reference to vector B
 */
inline void
MatrixMult( std::vector<float> &vector_c, utility::Matrix const &matrix_a, std::vector<float> const &vector_b ) {

    assert( matrix_a.col == vector_b.size( ) );

    float const *a { matrix_a.val.data( ) };
    float const *b { vector_b.data( ) };
    float *      c { vector_c.data( ) };

    float alpha { 1.0f };
    float beta { 0.0f };

    cblas_sgemv( CblasRowMajor, CblasNoTrans, matrix_a.row, matrix_a.col, alpha, a, matrix_a.col, b, 1, beta, c, 1 );
}

/**
 * @brief Function to compute the inversion of a matrix using floats
 *
 * @see http://www.netlib.org/lapack/lapacke.html
 * @see
 * http://www.netlib.org/lapack/explore-html/d2/d96/lapacke__dgetrf_8c_source.html
 * @see
 * http://www.netlib.org/lapack/explore-html/da/d0e/lapacke__dgetri_8c_source.html
 * @see
 * https://stackoverflow.com/questions/3519959/computing-the-inverse-of-a-matrix-using-lapack-in-c
 *
 * @param[in,out] matrix_a Matrix to be inverted
 */
inline void ComputeInverse( utility::Matrix &matrix_a ) {

    // A must be a square matrix???
    int              n { matrix_a.row };
    float *          a = matrix_a.val.data( );
    std::vector<int> piv( n + 1, 0 );

    int info {};
    info = LAPACKE_sgetrf( LAPACK_ROW_MAJOR, n, n, a, n, piv.data( ) );

    if ( info != 0 ) {
        throw std::runtime_error( "Issue with LAPACKE_sgetrf.\n" );
    }

    info = LAPACKE_sgetri( LAPACK_ROW_MAJOR, n, a, n, piv.data( ) );

    if ( info != 0 ) {
        throw std::runtime_error( "Issue with LAPACKE_sgetri.\n" );
    }
}

/**
 * @brief Normal distribution random number generator
 *
 * @param[in] random_numbers Stores random numbers
 * @param[in] lBound Lower bound of distribution
 * @param[in] uBound Upper bound of distribution
 */
inline void GenerateRandomNum( std::vector<float> &random_numbers, float const &lBound, float const &uBound ) {

    // Random device class instance, source of 'true' randomness for
    // initializing random seed
    std::default_random_engine gen( std::random_device {}( ) );

    // Instance of class std::normal_distribution with specific mean and stddev
    // Lower and Upper limits are declared in base class
    std::normal_distribution<float> distr( lBound, uBound );

    // Compute random numbers for all particles
    for_each( random_numbers.begin( ), random_numbers.end( ), [&distr, &gen]( float &a ) { a = distr( gen ); } );
}

/**
 * @brief Compute median for Monte Carlos times
 *
 * @param[in/out] time_vector Vector holding times for all Monte Carlos
 * @param[out] median Median time for all Monte Carlos
 */
inline void ComputeMedianTime( std::vector<float> &time_vector, float &median ) {
    // Compute median
    std::nth_element( time_vector.begin( ), time_vector.begin( ) + time_vector.size( ) / 2, time_vector.end( ) );
    median = time_vector[time_vector.size( ) / 2];

    // Remove outliers
    time_vector.erase( std::remove_if( time_vector.begin( ),
                                       time_vector.end( ),
                                       [&median]( float const &n ) { return ( n > ( median * 1.05f ) ); } ),
                       time_vector.end( ) );
}

/**
 * @brief Compute mean for Monte Carlos times
 *
 * @param[in] time_vector Vector holding times for all Monte Carlos
 * @param[out] mean Mean time for all Monte Carlos
 */
inline void ComputeMeanTime( std::vector<float> const &time_vector, float &mean ) {
    // Compute mean
    mean = std::accumulate( time_vector.begin( ), time_vector.end( ), 0.0f ) / time_vector.size( );
}

/**
 * @brief Compute standard deviation for Monte Carlos times
 *
 * @param[in] time_vector Vector holding times for all Monte Carlos
 * @param[in] mean Mean time for all Monte Carlos
 * @param[out] stdDev
 */
inline void ComputeStdDevTime( std::vector<float> const &time_vector, float const &mean, float &stdDev ) {

    // Compute standard deviation
    std::vector<float> diff( time_vector.size( ) );
    std::transform(
        time_vector.begin( ), time_vector.end( ), diff.begin( ), [&mean]( float const &x ) { return ( x - mean ); } );

    // Compute difference
    float sqrtSum { std::inner_product( diff.begin( ), diff.end( ), diff.begin( ), 0.0f ) };  // Compute product
    stdDev = std::sqrt( sqrtSum / time_vector.size( ) );
}

/**
 * @brief Compute median, mean, standard deviation for Monte Carlo times
 *
 * @param[in/out] time_vector
 * @param[in/out] median
 * @param[in/out] mean
 * @param[in/out] stdDev
 */
inline void ComputeOverallTiming( std::vector<float> &timeVec, float &median, float &mean, float &stdDev ) {
    ComputeMedianTime( timeVec, median );
    ComputeMeanTime( timeVec, mean );
    ComputeStdDevTime( timeVec, mean, stdDev );
}

/**
 * @brief Compute overall timing across all Monte Carlos
 *
 * @param[in] timing_results 2D vector containing timing results for each Monte
 * Carlo
 */
inline void PrintTimingResults( std::vector<std::vector<float>> const &timing_results ) {

    for ( int i = 0; i < static_cast<int>( utility::Timing::kCount ); i++ ) {
        std::printf( "%s\t ", utility::timing_map.find( utility::Timing( i ) )->second.c_str( ) );
    }
    std::printf( "\n" );

    for ( int i = 0; i < static_cast<int>( utility::Timing::kCount ); i++ ) {
        float sum { std::accumulate( timing_results[i].begin( ), timing_results[i].end( ), 0.0f ) };
        float mean { sum / timing_results[i].size( ) };
        std::printf( "%0.0f\t ", mean );
    }
    std::printf( "\n\n" );
}

/**
 * @brief Writes data to output file for generated system
 *        and measurement truth data
 *
 * @param[in] filename Output filename
 * @param[in] dim Number of system dimensions
 * @param[in] samples Number of samples
 * @param[in] it Iterator to beginning of vector to be printed
 */
inline void WriteToFile( std::string const &                          filename,
                         int const &                                  dim,
                         int const &                                  samples,
                         typename std::vector<float>::const_iterator &it ) {

    // We jump to the end to add each Monte Carlo
    std::ofstream fout( filename, std::ios_base::app );
    fout.precision( 15 );
    fout.setf( std::ios::fixed, std::ios::floatfield );
    // fout << dim << " " << samples << "\n";
    for ( int i = 0; i < samples; i++ ) {
        int idxS { i * dim };
        int idxE { idxS + dim };
        std::copy( it + idxS, it + idxE, std::ostream_iterator<float>( fout, " " ) );
        fout << "\n";
    }
    fout.close( );
}

/**
 * @brief Writes data to output file for filtered estimates
 *
 * @param[in] filename Output filename
 * @param[in] type Filter type
 * @param[in] samples Number of samples
 * @param[in] median Timing median across all Monte Carlos
 * @param[in] mean Timing mean across all Monte Carlos
 * @param[in] stdDev Timing standard deviation across all Monte Carlos
 * @param[in] it Iterator to beginning of vector to be printed
 */
inline void WriteToFile( std::string const &                          filename,
                         std::string const &                          type,
                         int const &                                  samples,
                         float const &                                median,
                         float const &                                mean,
                         float const &                                stdDev,
                         typename std::vector<float>::const_iterator &it ) {

    // We jump to the end to add each Monte Carlo
    std::ofstream fout( filename, std::ios_base::app );
    fout.setf( std::ios::fixed, std::ios::floatfield );
    fout << type << " " << median << " " << mean << " " << stdDev << "\n";
    fout.precision( 15 );
    for ( int i = 0; i < samples; i++ ) {
        int idxS { i * utility::kSysDim };
        int idxE { idxS + utility::kSysDim };
        std::copy( it + idxS, it + idxE, std::ostream_iterator<float>( fout, " " ) );
        fout << "\n";
    }
    fout.close( );
}

/**
 * @brief Write header to output file for generated system
 *        and measurement truth data
 *
 * @param[in] filename Output filename
 * @param[in] info Struct containing filter information
 */
inline void WriteTruthHeader( std::string const &filename, int const &num_mcs, int const &dim, int const &samples ) {
    std::ofstream fout( filename, std::ios_base::trunc );
    fout.setf( std::ios::fixed, std::ios::floatfield );
    fout << num_mcs << " " << dim << " " << samples << "\n";
    fout.close( );
}

/**
 * @brief Write header to output file.
 *
 * @param[in] filename Output filename
 * @param[in] info Struct containing filter information
 */
inline void WriteDataHeader( std::string const &filename, utility::FilterInfo const &filter_info ) {
    std::ofstream fout( filename, std::ios_base::trunc );
    fout.setf( std::ios::fixed, std::ios::floatfield );
    fout << filter_info.num_mcs << " " << kSysDim << " " << filter_info.samples << " " << filter_info.particles << "\n";
    fout.close( );
}

#ifdef USE_NVTX
#include "nvtx3/nvToolsExt.h"

uint32_t const colors[] { 0x0000ff00, 0x0f0000ff, 0x000000ff, 0x00ffff00, 0x00ff00ff,
                          0x0000ffff, 0x00ff0000, 0x00ffffff, 0x0f0f0f0f };
int const      num_colors { sizeof( colors ) / sizeof( uint32_t ) };

/**
 * class Tracer
 */
class Tracer {
  public:
    /**
     * @brief Construct a new Tracer object
     *
     * @see
     * https://docs.nvidia.com/gameworks/content/gameworkslibrary/nvtx/nvidia_tools_extension_library_nvtx.htm
     * @see
     * https://devblogs.nvidia.com/cuda-pro-tip-generate-custom-application-profile-timelines-nvtx/
     *
     * @param[in] name Function name
     * @param[in] cid color id
     */

    Tracer( char const *name, int const &cid ) {
        int color_id                      = cid;
        color_id                          = color_id % num_colors;
        nvtxEventAttributes_t eventAttrib = {};
        eventAttrib.version               = NVTX_VERSION;
        eventAttrib.size                  = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
        eventAttrib.colorType             = NVTX_COLOR_ARGB;
        eventAttrib.color                 = colors[color_id];
        eventAttrib.messageType           = NVTX_MESSAGE_TYPE_ASCII;
        eventAttrib.message.ascii         = name;
        nvtxRangePushEx( &eventAttrib );
    }

    /**
     * @brief Destroy the Tracer object
     *
     */
    ~Tracer( ) {
        nvtxRangePop( );
    }
};

#define RANGE( name, cid ) utility::Tracer uniq_name_using_macros( name, cid );
#else
#define RANGE( name, cid )
#endif

} /* namespace utility */

#endif /* UTILITIES_H_ */


#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <omp.h>
#include <stdbool.h>
#include <getopt.h>
#include <math.h>
#include <mpi.h>
#include "parmt_config.h"
#ifdef PARMT_USE_INTEL
#include <ippcore.h>
#include <mkl.h>
#include <mkl_cblas.h>
#else
#include <cblas.h>
#endif
#include "compearth.h"
#include "parmt_mtsearch.h"
#include "parmt_postProcess.h"
#include "parmt_utils.h"
#include "iscl/array/array.h"
#include "iscl/memory/memory.h"

#define PROGRAM_NAME "parmt"
static int parseArguments(int argc, char *argv[],
                          const int nprocs,
                          int *npInObsGroups,
                          int *npInLocGroups,
                          int *npInMTGroups,
                          char iniFile[PATH_MAX]);
static void printUsage(void);
int parmt_freeData(struct parmtData_struct *data);
int parmt_freeLocalMTs(struct localMT_struct *mts);
/*!
 *
 */
int main(int argc, char *argv[])
{
    char iniFile[PATH_MAX];
    double *betas, *h, *kappas, *gammas, *M0s, *sigmas, *thetas, *u, *v;
    double *deps, *luneMPDF, *phi, t0, t1, du, dv, dh, dk, ds;
    int64_t ngridSearch;
    double hLower, hUpper, uLower, uUpper, vLower, vUpper, xnorm;
    int *lags, i, ierr, iobs, myid, npInLocGroups, nmt, npInMTGroups,
        npInObsGroups, nprocs, provided;
    int ix, iy, k;
    MPI_Comm mtComm, locComm, obsComm;
    bool linMTComm, linLocComm, linObsComm;
    const int master = 0;
    const int ldm = 8;
struct parmtGeneralParms_struct parms;
struct parmtData_struct data;
struct parmtMtSearchParms_struct mtsearch;
    struct localMT_struct mtloc;
    //------------------------------------------------------------------------//
    //
    // initialize MPI, ISCL, and handle threading issues
    MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided);
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
    iscl_init();
#ifdef PARMT_USE_INTEL
    //omp_set_num_threads(2);
    mkl_set_num_threads(1);
    ippSetNumThreads(1);
#endif
    // initialize
    phi = NULL;
    M0s = NULL;
    betas = NULL;
    gammas = NULL;
    sigmas = NULL;
    kappas = NULL;
    thetas = NULL;
    h = NULL;
    u = NULL;
    v = NULL;
    lags = NULL;
    memset(&parms, 0, sizeof(struct parmtGeneralParms_struct));
    memset(&data, 0, sizeof(struct parmtData_struct));
    memset(&mtsearch, 0, sizeof(struct parmtMtSearchParms_struct));
    memset(&mtloc, 0, sizeof(struct localMT_struct));
    if (myid == master)
    {
        ierr = parseArguments(argc, argv, nprocs,
                              &npInObsGroups, &npInLocGroups, &npInMTGroups,
                              iniFile);
        if (ierr != 0){goto INIT_ERROR;}
        // read the ini file
        printf("%s: Parsing ini file %s...\n", PROGRAM_NAME, iniFile);
        ierr = 0;
        ierr += parmt_utils_readGeneralParms(iniFile, &parms);
        strcpy(parms.programName, PROGRAM_NAME);
        parms.programID = PARMT_ID;
        ierr += parmt_utils_readMtSearch(iniFile, &mtsearch);
        if (ierr != 0)
        {
            printf("%s: Error reading ini file\n", PROGRAM_NAME);
            goto INIT_ERROR;
        }
        printf("%s: Process management:\n", PROGRAM_NAME);
        printf("        Number of processes in observation groups %d\n",
               npInObsGroups);
        printf("        Number of processes in location groups %d\n",
               npInLocGroups);
        printf("        Number of processes in moment tensor groups %d\n",
               npInMTGroups);
//        npInObsGroups = 1;
//        npInLocGroups = 1;
//        npInMTGroups = nprocs;
printf("verify greens fns are correct\n");
/*
        double pAxis[3], nAxis[3], tAxis[3], beta, gamma, u,v;
        u= 3.0*M_PI/8.0;
        v=-1.0/9.0;
        compearth_u2beta(1, 10, 2, &u, 1.e-10, &beta);
        compearth_v2gamma(1, &v, &gamma);
        postprocess_tt2tnp(beta, gamma, 4.0*M_PI/5.0, -M_PI/2.0, 0.723,
                           pAxis, nAxis, tAxis);
        int nxp = 51;
        double *xw1 = memory_calloc64f(nxp*nxp);
        double *yw1 = memory_calloc64f(nxp*nxp);
        int8_t *pn1 = (int8_t *) calloc(nxp*nxp, sizeof(int8_t));
        const double xc = 1.5; const double yc = 1.5; const double rad = 1.0;
        // Compute the corresponding moment tensor
        double M0loc = 1.0/sqrt(2.0), Muse[6], lamUse[9], Uuse[9];
        double gammaDeg = gamma*180.0/M_PI;
        double deltaDeg = (M_PI_2 - beta)*180.0/M_PI;
        double kappaDeg = (4.0*M_PI/5.0)*180.0/M_PI;
        double sigmaDeg = (-M_PI_2)*180.0/M_PI;
        double thetaDeg = 0.723*180.0/M_PI;
        compearth_tt2cmt(gammaDeg, deltaDeg, M0loc,
                         kappaDeg, thetaDeg,
                         sigmaDeg,
                         Muse, lamUse, Uuse);
        printf("mt = [%f,%f,%f,%f,%f,%f]\n", Muse[0], Muse[1], Muse[2], Muse[3], Muse[4], Muse[5]);
printf("drawing\n");
for (int i=0; i<10000; i++)
{
        postprocess_tnp2beachballPolarity(nxp, xc, yc, rad,
                                          pAxis, nAxis, tAxis,
                                          xw1, yw1, pn1);
}
        FILE *fwork = fopen("depmag/beachball.txt", "w");
        for (int iy=0; iy<nxp; iy++)
        {
            for (int ix=0; ix<nxp; ix++)
            {
                int k = iy*nxp + ix;
                fprintf(fwork, "%e %e %d\n", xw1[k], yw1[k], pn1[k]);
            }
            fprintf(fwork, "\n");
        }
        fclose(fwork);
        memory_free64f(&xw1);
        memory_free64f(&yw1);
        free(pn1);
printf("back\n");
//getchar();
*/
    }
INIT_ERROR:;
    MPI_Bcast(&ierr, 1, MPI_INT, master, MPI_COMM_WORLD);
    if (ierr != 0){goto FINISH;}
    MPI_Bcast(&npInObsGroups, 1, MPI_INT, master, MPI_COMM_WORLD);
    MPI_Bcast(&npInLocGroups, 1, MPI_INT, master, MPI_COMM_WORLD);
    MPI_Bcast(&npInMTGroups,  1, MPI_INT, master, MPI_COMM_WORLD);
    // split the communicators
    ierr = parmt_splitComm(MPI_COMM_WORLD,
                           npInObsGroups, npInLocGroups, npInMTGroups,
                           &linObsComm, &obsComm,
                           &linLocComm, &locComm,
                           &linMTComm,  &mtComm);
    if (ierr != 0)
    {
        printf("%s: Fatal error splitting communicators - have a nice day\n",
               PROGRAM_NAME);
        MPI_Abort(MPI_COMM_WORLD, 30);
    }
    // load the observations and greens functions then broadcast
    if (myid == master)
    {
        printf("%s: Loading data...\n", PROGRAM_NAME);
        memset(&data, 0, sizeof(struct parmtData_struct));
        ierr = utils_dataArchive_readAllWaveforms(parms.dataFile, &data);
        data.est = (struct sacData_struct *)
                   calloc((size_t) data.nobs, sizeof(struct sacData_struct));
        ngridSearch = (int64_t) (mtsearch.nm*mtsearch.nb*mtsearch.ng)
                     *(int64_t) (mtsearch.nk*mtsearch.ns*mtsearch.nt)
                     *(int64_t) data.nlocs;
        if (ngridSearch > INT_MAX)
        {
            printf("%s: Error - grid search too big - integer overflow",
                   PROGRAM_NAME);
            ierr = 1;
        }
    }
    MPI_Bcast(&ierr, 1, MPI_INT, master, MPI_COMM_WORLD);
    if (ierr != 0){goto FINISH;}
    // Send the inputs to everyone
    if (myid == master)
    {
        printf("%s: Broadcasting parameters...\n", PROGRAM_NAME);
    }
    ierr = parmt_broadcast_mtSearchParms(&mtsearch, master, MPI_COMM_WORLD);
    if (ierr != 0)
    {
        printf("%s: Fatal error broadcasting parameters\n", PROGRAM_NAME);
        MPI_Abort(MPI_COMM_WORLD, 30);
    }
    ierr = parmt_broadcast_generalParms(&parms, master, MPI_COMM_WORLD);
    if (ierr != 0)
    {
        printf("%s: Fatal error broadcasting general parameters\n",
               PROGRAM_NAME);
        MPI_Abort(MPI_COMM_WORLD, 30);
    }
    // Send the waveforms to everyone
    ierr = parmt_broadcast_data(&data, master, MPI_COMM_WORLD);
    if (ierr != 0)
    {
        printf("%s: Fatal error broadcasting waveforms\n", PROGRAM_NAME);
        MPI_Abort(MPI_COMM_WORLD, 30);
    }
    // Discretize the moment tensor space in (u, v, h) space
    if (myid == master)
    {
        printf("%s: Discretizing MT space...\n", PROGRAM_NAME);
    }
    // Discretize the moment tensor space in (u, v, h) space
    ierr = parmt_discretizeCells64f(
               mtsearch.nb, mtsearch.betaLower,  mtsearch.betaUpper,
               mtsearch.ng, mtsearch.gammaLower, mtsearch.gammaUpper,
               mtsearch.nk, mtsearch.kappaLower, mtsearch.kappaUpper,
               mtsearch.ns, mtsearch.sigmaLower, mtsearch.sigmaUpper,
               mtsearch.nt, mtsearch.thetaLower, mtsearch.thetaUpper,
               mtsearch.nm, mtsearch.m0Lower,    mtsearch.m0Upper,
               mtsearch.luseLog,
               &betas,  &gammas, &kappas,
               &sigmas, &thetas, &M0s);
    if (ierr != 0)
    {
        printf("%s: Error discretizing MT space\n", PROGRAM_NAME);
        MPI_Abort(MPI_COMM_WORLD, 30);
    }
/*
    compearth_beta2u(1, &mtsearch.betaLower, &uLower);
    compearth_beta2u(1, &mtsearch.betaUpper, &uUpper);
    compearth_gamma2v(1, &mtsearch.gammaLower, &vLower);
    compearth_gamma2v(1, &mtsearch.gammaUpper, &vUpper);
    compearth_theta2h(1, &mtsearch.thetaLower, &hLower);
    compearth_theta2h(1, &mtsearch.thetaUpper, &hUpper);
    du = (uUpper - uLower)/(double) mtsearch.nb;
    dv = (vUpper - vLower)/(double) mtsearch.ng;
    dh =-(hUpper - hLower)/(double) mtsearch.nt;
    dk = (mtsearch.kappaUpper - mtsearch.kappaLower)/(double) mtsearch.nk;
    ds = (mtsearch.sigmaUpper - mtsearch.sigmaLower)/(double) mtsearch.ns;
//if (myid == master){printf("%f %f %f\n", du, dv, dh);}
    u = array_linspace64f(uLower+du/2, uUpper-du/2, mtsearch.nb, &ierr);
    v = array_linspace64f(vLower+dv/2, vUpper-dv/2, mtsearch.ng, &ierr);
    h = array_linspace64f(hLower-dh/2, hUpper+dh/2, mtsearch.nt, &ierr);
    betas  = memory_calloc64f(mtsearch.nb);
    gammas = memory_calloc64f(mtsearch.ng);
    thetas = memory_calloc64f(mtsearch.nt);
    compearth_u2beta(mtsearch.nb, 20, 2, u, 1.e-6, betas);
    compearth_v2gamma(mtsearch.ng, v, gammas);
    compearth_h2theta(mtsearch.nt, h, thetas);
    M0s    = array_linspace64f(mtsearch.m0Lower,    mtsearch.m0Upper,
                               mtsearch.nm, &ierr);
    kappas = array_linspace64f(mtsearch.kappaLower+dk/2, mtsearch.kappaUpper-dk/2,
                               mtsearch.nk, &ierr);
    sigmas = array_linspace64f(mtsearch.sigmaLower+ds/2, mtsearch.sigmaUpper-ds/2,
                               mtsearch.ns, &ierr);
*/
//if (myid != master){for (int i=0; i<mtsearch.nb; i++){printf("%f %f\n", u[i],betas[i]);}}
    // avoid an annoying warning
    for (i=0; i<mtsearch.ns; i++)
    {
        if (fabs(sigmas[i]) < 1.e-7){sigmas[i] = 1.e-7;}
    }
    nmt = mtsearch.ng*mtsearch.nb*mtsearch.nk
         *mtsearch.ns*mtsearch.nt*mtsearch.nm;
    if (myid == master)
    {
        printf("%s: Discretizing moment tensor...\n", PROGRAM_NAME);
    }
    t0 = MPI_Wtime();
    ierr = parmt_discretizeMT64f_MPI(mtComm,
                                     mtsearch.ng, gammas,
                                     mtsearch.nb, betas,
                                     mtsearch.nm, M0s,
                                     mtsearch.nk, kappas,
                                     mtsearch.nt, thetas,
                                     mtsearch.ns, sigmas,
                                     ldm, &mtloc);
    if (ierr != 0)
    {
        printf("%s: Error discretizing moment tensor on process %d\n",
               PROGRAM_NAME, myid);
        MPI_Abort(MPI_COMM_WORLD, 30);
    }
    MPI_Barrier(MPI_COMM_WORLD);
    t1 = MPI_Wtime();
    if (myid == master)
    {
        printf("%s: Discretization time %6.2f (seconds)\n",
               PROGRAM_NAME, t1 - t0);
    }
    // Initialize the  output file
    t0 = MPI_Wtime();
    if (myid == master)
    {
        printf("%s: Initializing output file...\n", PROGRAM_NAME);
        deps = memory_calloc64f(data.nlocs);
        for (i=0; i<data.nlocs; i++)
        {
            deps[i] = data.sacGxx[i].header.evdp;
        }
        ierr = parmt_io_createObjfnArchive64f(//parms.resultsDir, parms.projnm,
                                              //parms.resultsFileSuffix,
                                              PROGRAM_NAME, parms.parmtArchive,
                                              data.nobs,
                                              data.nlocs, deps,
                                              mtsearch.nm, M0s,
                                              mtsearch.nb, betas,
                                              mtsearch.ng, gammas,
                                              mtsearch.nk, kappas,
                                              mtsearch.ns, sigmas,
                                              mtsearch.nt, thetas);
        memory_free64f(&deps);
    }
    MPI_Barrier(MPI_COMM_WORLD);
    // Compute the objective functions
    t0 = MPI_Wtime();
    phi = NULL;
    lags = NULL;
/*
    nlags = 0;
double lagTime;
bool ldefault, lwantLags;
int iobs = 0;
int nlags = 0;
    phi = NULL;
    lags = NULL;
    nlags = 0;
    if (parms.lwantLags)
    {
        //lagTime = data.data[iobs].header.user0;
        //if (lagTime < 0.0){lagTime = parms.defaultMaxLagTime;}
        lagTime = parmt_utils_getLagTime(data.data[iobs],
                                         parms.defaultMaxLagTime,
                                         &ldefault);
        nlags = (int) (lagTime/data.data[iobs].header.delta + 0.5);
    }
    lags = NULL;
    lwantLags = false;
    if (nlags > 0){lwantLags = true;}
*/
    if (myid == master)
    {
        phi = memory_calloc64f(data.nlocs*mtloc.nmtAll);
    }
    else
    {
        phi = memory_calloc64f(1);
    }
    // Perform the grid search
    MPI_Wtime();
    ierr = parmt_obsSearch64f(MPI_COMM_WORLD,
                              obsComm, locComm,
                              linObsComm, linLocComm,
                              parms, data, mtloc, phi);
    if (ierr != 0)
    {
        printf("%s: Error calling obsSearch64f\n", PROGRAM_NAME);
        MPI_Abort(MPI_COMM_WORLD, 30);
    }
    MPI_Barrier(MPI_COMM_WORLD);
    if (myid == master)
    {
        MPI_Wtime();
        printf("%s: Objective function computation time: %f\n",
               PROGRAM_NAME, MPI_Wtime() - t0);
    }
    if (ierr != 0)
    {
        printf("%s: Error calling locSearchXC64f\n", PROGRAM_NAME);
        MPI_Abort(MPI_COMM_WORLD, 30);
    }
    if (myid == master)
    {
        printf("%s: Writing results...\n", PROGRAM_NAME);
        ierr = parmt_io_writeObjectiveFunction64f(
                   //parms.resultsDir, parms.projnm, parms.resultsFileSuffix,
                   parms.parmtArchive, nmt, phi);
        if (ierr != 0)
        {
            printf("%s: Error writing objective function\n", PROGRAM_NAME);
            goto FINISH;
        }
        int jloc, jm, jb, jg, jk, js, jt;
        int imtopt = array_argmax64f(data.nlocs*mtloc.nmtAll, phi, &ierr);
        marginal_getOptimum(data.nlocs, mtsearch.nm, mtsearch.nb,
                            mtsearch.ng, mtsearch.nk, mtsearch.ns,
                            mtsearch.nt, phi,
                            &jloc, &jm, &jb, &jg,
                            &jk, &js, &jt);
        printf("%d %f %f %f %f %f %f\n", jloc,
               gammas[jg]*180.0/M_PI, 90.0-betas[jb]*180.0/M_PI, 1.0,
               kappas[jk]*180.0/M_PI, thetas[jt]*180.0/M_PI,
               sigmas[js]*180.0/M_PI);
        double Muse[6], Mned[6], lam[3], U[9];
        printf("phi opt: %d %e\n", imtopt, phi[imtopt]);
        printf("%d %d %d %d %d %d %d\n", jloc, jg, jb, jm, jk, jt, js);
        double gammaOpt = gammas[jg]*180.0/M_PI;
        double deltaOpt = 90.0-betas[jb]*180.0/M_PI;
        double kappaOpt = kappas[jk]*180.0/M_PI;
        double thetaOpt = thetas[jt]*180.0/M_PI;
        double sigmaOpt = sigmas[js]*180.0/M_PI;
        compearth_TT2CMT(1, &gammaOpt,
                         &deltaOpt,
                         &M0s[jm],
                         &kappaOpt,
                         &thetaOpt,
                         &sigmaOpt,
                         Muse, lam, U);
/*
        compearth_tt2cmt(gammas[jg]*180.0/M_PI,
                         90.0-betas[jb]*180.0/M_PI,
                         M0s[jm],
                         kappas[jk]*180.0/M_PI,
                         thetas[jt]*180.0/M_PI,
                         sigmas[js]*180.0/M_PI,
                         Muse, lam, U);
*/
        parmt_discretizeMT64f(1, &gammas[jg],
                              1, &betas[jb],
                              1, &M0s[jm],
                              1, &kappas[jk],
                              1, &thetas[jt],
                              1, &sigmas[js],
                              6, 1, Mned);
        //printf("%e %e %e\n", phi[0], phi[1000], phi[500000]);
        printf("mtUSE =[%f,%f,%f,%f,%f,%f]\n",
               Muse[0], Muse[1], Muse[2], Muse[3], Muse[4], Muse[5]);
        printf("mtNED =[%f,%f,%f,%f,%f,%f]\n",
               Mned[0], Mned[1], Mned[2], Mned[3], Mned[4], Mned[5]);
    }
FINISH:;
    if (linObsComm){MPI_Comm_free(&obsComm);}
    if (linLocComm){MPI_Comm_free(&locComm);}
    if (linMTComm){MPI_Comm_free(&mtComm);}
    memory_free64f(&M0s);
    memory_free64f(&h);
    memory_free64f(&u);
    memory_free64f(&v);
    memory_free64f(&betas);
    memory_free64f(&gammas);
    memory_free64f(&kappas);
    memory_free64f(&sigmas);
    memory_free64f(&thetas);
    memory_free64f(&phi);
    memory_free32i(&lags);
    parmt_freeData(&data);
    parmt_freeLocalMTs(&mtloc);
    iscl_finalize();
    MPI_Finalize();
    return EXIT_SUCCESS;
}

static int parseArguments(int argc, char *argv[],
                          const int nprocs,
                          int *npInObsGroups,
                          int *npInLocGroups,
                          int *npInMTGroups,
                          char iniFile[PATH_MAX])
{
    bool linFile;
    int prod;
    linFile = false;
    *npInObsGroups = 1;
    *npInLocGroups = 1;
    *npInMTGroups = 1;
    memset(iniFile, 0, PATH_MAX*sizeof(char));
    while (true)
    {
        static struct option longOptions[] =
        {
            {"help", no_argument, 0, '?'},
            {"help", no_argument, 0, 'h'},
            {"ini_file", required_argument, 0, 'i'},
            {"obsGroupSize", required_argument, 0, 'w'},
            {"mtGroupSize",  required_argument, 0, 'm'},
            {"locGroupSize", required_argument, 0, 'l'},
            {0, 0, 0, 0}
        };
        int c, optionIndex;
        c = getopt_long(argc, argv, "?hi:w:m:l:",
                        longOptions, &optionIndex);
        if (c ==-1){break;}
        if (c == 'i')
        {
            strcpy(iniFile, (const char *) optarg);
            linFile = true;
        }
        else if (c == 'w' && nprocs > 1)
        {
            *npInObsGroups = atoi(optarg);
        }
        else if (c == 'l' && nprocs > 1)
        {
            *npInLocGroups = atoi(optarg);
        }
        else if (c == 'm' && nprocs > 1)
        {
            *npInMTGroups = atoi(optarg);
        }
        else if (c == 'h' || c == '?')
        {
            printUsage();
            return -2;
        }
        else
        {
            printf("%s: Unknown options: %s\n",
                   PROGRAM_NAME, argv[optionIndex]);
        }
    }
    if (!linFile)
    {
        printf("%s: Error must specify ini file\n\n", PROGRAM_NAME);
        printUsage();
        return -1;
    }
    prod = (*npInObsGroups)*(*npInLocGroups)*(*npInMTGroups);
    if (prod != nprocs)
    {
        if (prod != 1)
        {
            printf("%s: Invalid process layout - defaulting to (%d,%d,%d)\n",
                   PROGRAM_NAME, 1, 1, nprocs);
        }
        *npInObsGroups = 1;
        *npInLocGroups = 1;
        *npInMTGroups = nprocs;
    }
    return 0;
}

static void printUsage(void)
{
    printf("Usage:\n   parmt -i input_file\n\n");
    printf("Required arguments:\n");
    printf("   -i input_file specifies the initialization file\n");
    printf("\n");
    printf("Optional arguments:\n");
    printf("   -m number of processes in moment-tensor groups\n");
    printf("   -w number of processes in waveform (observation) groups\n");
    printf("   -l number of processes in location groups\n");
    printf("   -h displays this message\n");
    printf("   Note if m*w*l must equal the number of processes\n");
    printf("   If they are not set the program will set -m=nprocs\n");
    return;
}

int parmt_freeData(struct parmtData_struct *data)
{
    int i;
    if (data->nobs > 0 && data->nlocs > 0 &&
        data->sacGxx != NULL && data->sacGyy != NULL && data->sacGzz != NULL &&
        data->sacGxy != NULL && data->sacGxz != NULL && data->sacGyz != NULL)
    {
        for (i=0; i<data->nobs*data->nlocs; i++)
        {
            sacio_freeData(&data->sacGxx[i]);
            sacio_freeData(&data->sacGyy[i]);
            sacio_freeData(&data->sacGzz[i]);
            sacio_freeData(&data->sacGxy[i]);
            sacio_freeData(&data->sacGxz[i]);
            sacio_freeData(&data->sacGyz[i]);
        }
        free(data->sacGxx);
        free(data->sacGyy);
        free(data->sacGzz);
        free(data->sacGxy);
        free(data->sacGxz);
        free(data->sacGyz);
    }
    if (data->nobs > 0 && data->data != NULL)
    {
        for (i=0; i<data->nobs; i++)
        {
            sacio_freeData(&data->data[i]);
        }
        free(data->data);
    }
    if (data->nobs > 0 && data->est != NULL)
    {
        free(data->est);
    }
    memset(data, 0, sizeof(struct parmtData_struct));
    return 0;
}

int parmt_freeLocalMTs(struct localMT_struct *mtloc)
{
    memory_free64f(&mtloc->mts);
    //memory_free32i(&mtloc->l2g); // TODO remove
    memory_free32i(&mtloc->offset);
    memset(mtloc, 0, sizeof(struct localMT_struct));
    return 0;
}


#include <string.h>
#include <stdint.h>
#include <mpi.h>

#include <gsl/gsl_rng.h>
#include <fastpm/libfastpm.h>
#include <fastpm/logging.h>
#include <fastpm/string.h>
#include <fastpm/transfer.h>
#include "chealpix.h"
#include <mpsort.h>

#include "pmpfft.h"
#include "pmghosts.h"

static double RNDTABLE[8192];

gsl_rng * random_generator;
void fastpm_utils_init_randtable() {
    random_generator = gsl_rng_alloc(gsl_rng_ranlxd1);
    int rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    gsl_rng_set(random_generator, 37 * (rank + 1) + 1);  /* start-up seed */
    int i;
    for(i = 0; i < 8192; i ++) {
        RNDTABLE[i] = gsl_rng_uniform(random_generator);
    }
    //gsl_rng_free(random_generator);
}

double
fastpm_utils_get_random(uint64_t id)
{
    return gsl_rng_uniform(random_generator);
    uint64_t ind = 0;
    ind = id;

    while(id != 0) {
        ind += id;
        id /= 8192;
    }
    ind %= 8192;
    return RNDTABLE[ind];
}

void
fastpm_utils_dump(PM * pm , const char * filename, FastPMFloat *data)
{
    char fn1[1024];
    char fn2[1024];
    if(pm->NTask > 1) {
        sprintf(fn1, "%s.%03d.geometry", filename, pm->ThisTask);
        sprintf(fn2, "%s.%03d", filename, pm->ThisTask);
    } else {
        sprintf(fn1, "%s.geometry", filename);
        sprintf(fn2, "%s", filename);
    }

    fastpm_path_ensure_dirname(filename);

    FILE * fp;
    fp = fopen(fn2, "w");
    if(pm->allocsize != fwrite(data, sizeof(FastPMFloat), pm->allocsize, fp)) {
        fastpm_raise(-1, "IO failed.\n");
    }
    fclose(fp);
    fp = fopen(fn1, "w");
    fprintf(fp, "# real\n");
    fprintf(fp, "start: %td %td %td\n",
                    pm->IRegion.start[0],
                    pm->IRegion.start[1],
                    pm->IRegion.start[2]);
    fprintf(fp, "size: %td %td %td\n",
                    pm->IRegion.size[0],
                    pm->IRegion.size[1],
                    pm->IRegion.size[2]);
    fprintf(fp, "strides: %td %td %td\n",
                    pm->IRegion.strides[0],
                    pm->IRegion.strides[1],
                    pm->IRegion.strides[2]);

    fprintf(fp, "# complex\n");
    fprintf(fp, "start: %td %td %td\n",
                    pm->ORegion.start[0],
                    pm->ORegion.start[1],
                    pm->ORegion.start[2]);
    fprintf(fp, "size: %td %td %td\n",
                    pm->ORegion.size[0],
                    pm->ORegion.size[1],
                    pm->ORegion.size[2]);
    fprintf(fp, "strides: %td %td %td\n",
                    pm->ORegion.strides[0],
                    pm->ORegion.strides[1],
                    pm->ORegion.strides[2]);

    fclose(fp);
}

void
fastpm_utils_load(PM * pm , const char * filename, FastPMFloat *data)
{
    char fn1[1024];
    char fn2[1024];
    if(pm->NTask > 1) {
        sprintf(fn1, "%s.%03d.geometry", filename, pm->ThisTask);
        sprintf(fn2, "%s.%03d", filename, pm->ThisTask);
    } else {
        sprintf(fn1, "%s.geometry", filename);
        sprintf(fn2, "%s", filename);
    }
    FILE * fp;
    fp = fopen(fn2, "r");
    if(pm->allocsize != fread(data, sizeof(FastPMFloat), pm->allocsize, fp)) {
        fastpm_raise(-1, "File was bad\n");
    };
    fclose(fp);
}

static double
tk_eh(double k, struct fastpm_powerspec_eh_params * params)		/* from Martin White */
{
    double q, theta, ommh2, a, s, gamma, L0, C0;
    double tmp;
    double omegam, ombh2, hubble;

    /* other input parameters */
    hubble = params->hubble_param;
    omegam = params->omegam;
    ombh2 = params->omegab * hubble * hubble;

    theta = 2.728 / 2.7;
    ommh2 = omegam * hubble * hubble;
    s = 44.5 * log(9.83 / ommh2) / sqrt(1. + 10. * exp(0.75 * log(ombh2))) * hubble;
    a = 1. - 0.328 * log(431. * ommh2) * ombh2 / ommh2
        + 0.380 * log(22.3 * ommh2) * (ombh2 / ommh2) * (ombh2 / ommh2);
    gamma = a + (1. - a) / (1. + exp(4 * log(0.43 * k * s)));
    gamma *= omegam * hubble;
    q = k * theta * theta / gamma;
    L0 = log(2. * exp(1.) + 1.8 * q);
    C0 = 14.2 + 731. / (1. + 62.5 * q);
    tmp = L0 / (L0 + C0 * q * q);
    return (tmp);
}

double
fastpm_utils_powerspec_eh(double k, struct fastpm_powerspec_eh_params * param)	/* Eisenstein & Hu */
{
    return param->Norm * k * pow(tk_eh(k, param), 2);
}

double
fastpm_utils_powerspec_white(double k, double * amplitude) 	/* White Noise*/
{
    return *amplitude;
}

static void
_sort_pix(const void * ptr, void * radix, void * arg)
{
    memcpy(radix, ptr, 8);
}

/* generate evenly balanced healpix positions that are inside the lightcone. */
void
fastpm_utils_healpix_ra_dec (
                int nside,
                double **ra,
                double **dec,
                uint64_t **pix,
                size_t * n,
                FastPMLightCone * lc,
                MPI_Comm comm
            )
{
    const double rad_to_degree = 180./M_PI;

    size_t npix = nside2npix (nside);
    int ThisTask, NTask;

    MPI_Comm_rank(comm, &ThisTask);
    MPI_Comm_size(comm, &NTask);

    //fastpm_info("healpix npix %ld \n",*npix);
    size_t i = 0;

    size_t pix_start = ThisTask * npix / NTask;
    size_t pix_end = (ThisTask + 1) * npix / NTask;

    uint64_t local_npix = 0;

    uint64_t * pixels = NULL;

    /* two iterations; estimate and fill */
    while(1) {
        size_t j = 0;

        for (i = pix_start; i < pix_end; i++)
        {
            double vec[3];

            pix2vec_ring(nside, i, vec);

            if(!fastpm_lc_inside(lc, vec)) continue;

            if(pixels != NULL) {
                pixels[j] = i;
            }

            j ++;
        }
        if(pixels == NULL) {
            local_npix = j;
            pixels = malloc(sizeof(uint64_t) * local_npix);

        } else {
            break;
        }
    }

    /* count total */
    uint64_t valid_npix = local_npix;
    MPI_Allreduce(MPI_IN_PLACE, &valid_npix, 1, MPI_LONG, MPI_SUM, comm);

    /* redistribute / balance */

    size_t localsize =
        (ThisTask + 1) * valid_npix / NTask
    -
        (ThisTask    ) * valid_npix / NTask;

    uint64_t * recv_buffer = malloc(sizeof(uint64_t) * localsize);

    mpsort_mpi_newarray(pixels, local_npix, recv_buffer, localsize, sizeof(uint64_t), _sort_pix, 8, NULL, comm);

    free(pixels);

    *ra = malloc(sizeof(double) * localsize);
    *dec = malloc(sizeof(double) * localsize);

    for(i = 0; i < localsize; i ++) {
        double phi, theta;

        pix2ang_ring(nside, recv_buffer[i], &theta, &phi);

        phi *= rad_to_degree;
        theta*= rad_to_degree;
        (*ra)[i] = phi;
        (*dec)[i]= 90 - theta;
        recv_buffer[i] += (((uint64_t)nside) << 48);
    }
    *n = localsize;
    *pix = recv_buffer;
}

void
fastpm_gldot(double glmatrix[4][4], double xi[4], double xo[4])
{
    int i, j;
    for(i = 0; i < 4; i ++) {
        xo[i] = 0;
        for(j = 0; j < 4; j ++) {
            xo[i] += glmatrix[i][j] * xi[j];
        }
    }
}

void
fastpm_gldotf(double glmatrix[4][4], float vi[4], float vo[4])
{
    int i, j;
    for(i = 0; i < 4; i ++) {
        vo[i] = 0;
        for(j = 0; j < 4; j ++) {
            vo[i] += glmatrix[i][j] * vi[j];
        }
    }
}

/* returns MPI_LOR reduction */
int
MPIU_Any(MPI_Comm comm, int value)
{
    MPI_Allreduce(MPI_IN_PLACE, &value, 1, MPI_INT, MPI_LOR, comm);
    return value;
}

/* returns MPI_LAND reduction */
int
MPIU_All(MPI_Comm comm, int value)
{
    MPI_Allreduce(MPI_IN_PLACE, &value, 1, MPI_INT, MPI_LAND, comm);
    return value;
}

/*
 * Compute the standard statistics of a value over communicator.
 * fmt is a string, describing the list of stats to return:
 *  < : minimum  (double)
 *  , : rank of minimum (int)
 *  > : maximum  (double)
 *  . : rank of maximum (int)
 *  - : mean  (double)
 *  s : std (biased standard derivation)
 *  v : variance (biased variance)
 *  S : unbiased std
 *  V : unbiased variance
 *
 * e.g.
 *
 *    double min, max;
 *    MPIU_stats(comm, r, "<>", &min, &max);
 *
 * returns number of items converted;
 * if a fmt char is unknown, the input variable is unmodified.
 *
 * */
int
MPIU_stats(MPI_Comm comm,
    const double value,
    const char * fmt, ...)
{

    va_list va;
    va_start(va, fmt);

    int NTask;
    MPI_Comm_size(comm, &NTask);
    int ThisTask;
    MPI_Comm_rank(comm, &ThisTask);

    double sum = value;
    struct {
        double value;
        int rank;
    } min = {value, ThisTask},
      max = {value, ThisTask};

    double sumsq = value * value;

    MPI_Allreduce(MPI_IN_PLACE, &sum, 1, MPI_DOUBLE, MPI_SUM, comm);
    MPI_Allreduce(MPI_IN_PLACE, &sumsq, 1, MPI_DOUBLE, MPI_SUM, comm);
    MPI_Allreduce(MPI_IN_PLACE, &min, 1, MPI_DOUBLE_INT, MPI_MINLOC, comm);
    MPI_Allreduce(MPI_IN_PLACE, &max, 1, MPI_DOUBLE_INT, MPI_MAXLOC, comm);

    double avg = (sum) / NTask;
    double avg_sq = (sumsq) / NTask;
    double var = avg_sq - avg * avg;
    double std = sqrt(avg_sq - avg * avg);

    int i;
    int c = 0;
    for(i = 0; i < strlen(fmt); i ++) {
        void * r = va_arg(va, void *);
        int * ir = (int * ) r;
        double * dr = (double * ) r;

        c++;
        switch(fmt[i]) {
            case '-':
                *dr = avg;
                break;
            case '<':
                *dr = min.value;
                break;
            case '>':
                *dr = max.value;
                break;
            case ',':
                *ir = min.rank;
                break;
            case '.':
                *ir = max.rank;
                break;
            case 's':
                *dr = std;
                break;
            case 'S':
                *dr = std * sqrt(1.0 * NTask / (NTask - 1.0));
                break;
            case 'v':
                *dr = var;
                break;
            case 'V':
                *dr = var * 1.0 * NTask / (NTask - 1.0);
                break;
            default:
                c--;
        }
    }
    va_end(va);

    return c;
}

/*
 *
 * create a copy of string src and broadcast it to all ranks.
 *
 * Every rank receives a copy of src as the return value, including the root.
 *
 * if free is not NULL, the function is called as free(src)
 * */
char *
MPIU_Bcast_string(MPI_Comm comm, char * src, int root, void(*free)(void * ptr))
{
    int srcNULL = src == NULL;
    MPI_Bcast(&srcNULL, 1, MPI_INT, root, comm);

    if(srcNULL) return NULL;

    int rank;
    int N;
    MPI_Comm_rank(comm, &rank);

    if(rank == root) {
        N = strlen(src) + 1;
    }
    MPI_Bcast(&N, 1, MPI_INT, root, comm);

    char * ret = malloc(N);
    if(rank == root) {
        strcpy(ret, src);
    }

    if(free && rank == root) free(src);
    MPI_Bcast(ret, N, MPI_BYTE, root, comm);

    return ret;
}


/**
 *
 * @file qwrapper_claset2.c
 *
 *  PLASMA core_blas quark wrapper
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Hatem Ltaief
 * @date 2010-11-15
 * @generated c Tue Jan  7 11:44:59 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************//**
 *
 **/
void QUARK_CORE_claset2(Quark *quark, Quark_Task_Flags *task_flags,
                       PLASMA_enum uplo, int M, int N,
                       PLASMA_Complex32_t alpha, PLASMA_Complex32_t *A, int LDA)
{
    DAG_CORE_LASET;
    QUARK_Insert_Task(quark, CORE_claset2_quark, task_flags,
        sizeof(PLASMA_enum),                &uplo,  VALUE,
        sizeof(int),                        &M,     VALUE,
        sizeof(int),                        &N,     VALUE,
        sizeof(PLASMA_Complex32_t),         &alpha, VALUE,
        sizeof(PLASMA_Complex32_t)*M*N,     A,      OUTPUT,
        sizeof(int),                        &LDA,   VALUE,
        0);
}

/***************************************************************************//**
 *
 **/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_claset2_quark = PCORE_claset2_quark
#define CORE_claset2_quark PCORE_claset2_quark
#endif
void CORE_claset2_quark(Quark *quark)
{
    PLASMA_enum uplo;
    int M;
    int N;
    PLASMA_Complex32_t alpha;
    PLASMA_Complex32_t *A;
    int LDA;

    quark_unpack_args_6(quark, uplo, M, N, alpha, A, LDA);
    CORE_claset2(uplo, M, N, alpha, A, LDA);
}


/*---
  Flow*: A Verification Tool for Cyber-Physical Systems.
  Authors: Xin Chen, Sriram Sankaranarayanan, and Erika Abraham.
  Email: Xin Chen <chenxin415@gmail.com> if you have questions or comments.

  The code is released as is under the GNU General Public License (GPL).
---*/

#ifndef INCLUDE_H_
#define INCLUDE_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <cmath>
#include <mpfr.h>
#include <vector>
#include <string>
#include <list>
#include <iostream>
#include <cassert>
#include <map>
#include <time.h>
#include <algorithm>
#include <gsl/gsl_poly.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_linalg.h>
#include <glpk.h>

const int normal_precision	=	53;
const int high_precision	=	256;

#define MAX_REFINEMENT_STEPS	49
#define MAX_DYN_REF				49

#define MAX_WIDTH				1e-12

#define THRESHOLD_HIGH			1e-12
#define THRESHOLD_LOW			1e-20

#define STOP_RATIO				0.99
#define ABS_STOP_RATIO			0.99

#define PN 						15 			// the number of digits printed
#define INVALID 				-1e10
#define UNBOUNDED				1e10

#define DC_THRESHOLD_SEARCH 	1e-6
#define DC_THRESHOLD_IMPROV		0.9

#define ID_PRE			0
#define QR_PRE			1

#define UNIFORM			0
#define MULTI			1

#define LAMBDA_DOWN		0.5
#define LAMBDA_UP		1.1

#define NAME_SIZE		100

#define UNSAFE			-1
#define SAFE			0
#define UNKNOWN			1

#define COMPLETED_UNSAFE		1
#define COMPLETED_SAFE			2
#define COMPLETED_UNKNOWN		3
#define UNCOMPLETED_SAFE		4
#define UNCOMPLETED_UNSAFE		5
#define UNCOMPLETED_UNKNOWN		6

#define PLOT_GNUPLOT	0
#define PLOT_MATLAB		1

#define PLOT_INTERVAL	0
#define PLOT_OCTAGON	1
#define PLOT_GRID		2

#define INTERVAL_AGGREG	0
#define PARA_AGGREG		1
#define PCA_AGGREG		2

#define MSG_SIZE		100
#define NUM_LENGTH		50

#define ONLY_PICARD				1
#define LOW_DEGREE				2
#define HIGH_DEGREE				3
#define NONPOLY_TAYLOR			4
#define LTI						5
#define LTV						6
#define ONLY_PICARD_SYMB		7
#define NONPOLY_TAYLOR_SYMB		8

#define POLY_DYN		1
#define NONPOLY_DYN		2
#define LTI_DYN			3
#define LTV_DYN			4

#define REFINEMENT_PREC	1e-5

#define RESET_COLOR		"\033[0m"
#define BLACK_COLOR		"\033[30m"
#define RED_COLOR		"\033[31m"
#define GREEN_COLOR		"\033[32m"
#define BLUE_COLOR		"\033[34m"
#define BOLD_FONT		"\e[1m"

const char str_pi_up[]	=	"3.14159265358979323846264338327950288419716939937511";
const char str_pi_lo[]	=	"3.14159265358979323846264338327950288419716939937510";

const char outputDir[] = "./outputs/";
const char imageDir[] = "./images/";
const char counterexampleDir[] = "./counterexamples/";
const char local_var_name[] = "local_var_";

const char str_prefix_taylor_picard[] = "taylor picard { ";
const char str_prefix_taylor_remainder[] = "taylor remainder { ";
const char str_prefix_taylor_polynomial[] = "taylor polynomial { ";
const char str_prefix_center[] = "nonpolynomial center { ";

const char str_prefix_combination_picard[] = "combination picard { ";
const char str_prefix_combination_remainder[] = "combination remainder { ";
const char str_prefix_combination_polynomial[] = "combination polynomial { ";

const char str_prefix_univariate_polynomial[] = "univariate polynomial { ";
const char str_prefix_multivariate_polynomial[] = "multivariate polynomial { ";

const char str_prefix_expression[] = "expression { ";

const char str_prefix_matrix[] = "matrix { ";

const char str_suffix[] = " }";

const char str_counterexample_dumping_name_suffix[] = ".counterexample";

extern int lineNum;

extern void parseODE();

#endif /* INCLUDE_H_ */