/*
 * 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 Sun Nov  7 20:45:16 EST 1999 */

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

/* Generated by: ./genfft -magic-alignment-check -magic-twiddle-load-all -magic-variables 4 -magic-loopi -hc2hc-backward 16 */

/*
 * This function contains 298 FP additions, 134 FP multiplications,
 * (or, 244 additions, 80 multiplications, 54 fused multiply/add),
 * 49 stack variables, and 128 memory accesses
 */
static const fftw_real K1_961570560 = FFTW_KONST(+1.961570560806460898252364472268478073947867462);
static const fftw_real K390180644 = FFTW_KONST(+0.390180644032256535696569736954044481855383236);
static const fftw_real K1_111140466 = FFTW_KONST(+1.111140466039204449485661627897065748749874382);
static const fftw_real K1_662939224 = FFTW_KONST(+1.662939224605090474157576755235811513477121624);
static const fftw_real K707106781 = FFTW_KONST(+0.707106781186547524400844362104849039284835938);
static const fftw_real K382683432 = FFTW_KONST(+0.382683432365089771728459984030398866761344562);
static const fftw_real K923879532 = FFTW_KONST(+0.923879532511286756128183189396788286822416626);
static const fftw_real K765366864 = FFTW_KONST(+0.765366864730179543456919968060797733522689125);
static const fftw_real K1_847759065 = FFTW_KONST(+1.847759065022573512256366378793576573644833252);
static const fftw_real K1_414213562 = FFTW_KONST(+1.414213562373095048801688724209698078569671875);
static const fftw_real K2_000000000 = FFTW_KONST(+2.000000000000000000000000000000000000000000000);

/*
 * Generator Id's :
 * $Id: exprdag.ml,v 1.41 1999/05/26 15:44:14 fftw Exp $
 * $Id: fft.ml,v 1.43 1999/05/17 19:44:18 fftw Exp $
 * $Id: to_c.ml,v 1.25 1999/10/26 21:41:32 stevenj Exp $
 */

void fftw_hc2hc_backward_16(fftw_real *A, const fftw_complex *W, int iostride, int m, int dist)
{
     int i;
     fftw_real *X;
     fftw_real *Y;
     X = A;
     Y = A + (16 * iostride);
     {
	  fftw_real tmp279;
	  fftw_real tmp324;
	  fftw_real tmp312;
	  fftw_real tmp299;
	  fftw_real tmp276;
	  fftw_real tmp296;
	  fftw_real tmp309;
	  fftw_real tmp323;
	  fftw_real tmp283;
	  fftw_real tmp291;
	  fftw_real tmp286;
	  fftw_real tmp294;
	  fftw_real tmp301;
	  fftw_real tmp319;
	  fftw_real tmp327;
	  fftw_real tmp326;
	  fftw_real tmp316;
	  fftw_real tmp302;
	  ASSERT_ALIGNED_DOUBLE;
	  {
	       fftw_real tmp277;
	       fftw_real tmp278;
	       fftw_real tmp310;
	       fftw_real tmp297;
	       fftw_real tmp298;
	       fftw_real tmp311;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp277 = X[2 * iostride];
	       tmp278 = X[6 * iostride];
	       tmp310 = tmp277 - tmp278;
	       tmp297 = Y[-2 * iostride];
	       tmp298 = Y[-6 * iostride];
	       tmp311 = tmp298 + tmp297;
	       tmp279 = K2_000000000 * (tmp277 + tmp278);
	       tmp324 = K1_414213562 * (tmp310 + tmp311);
	       tmp312 = K1_414213562 * (tmp310 - tmp311);
	       tmp299 = K2_000000000 * (tmp297 - tmp298);
	  }
	  {
	       fftw_real tmp275;
	       fftw_real tmp308;
	       fftw_real tmp273;
	       fftw_real tmp306;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp274;
		    fftw_real tmp307;
		    fftw_real tmp271;
		    fftw_real tmp272;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp274 = X[4 * iostride];
		    tmp275 = K2_000000000 * tmp274;
		    tmp307 = Y[-4 * iostride];
		    tmp308 = K2_000000000 * tmp307;
		    tmp271 = X[0];
		    tmp272 = X[8 * iostride];
		    tmp273 = tmp271 + tmp272;
		    tmp306 = tmp271 - tmp272;
	       }
	       tmp276 = tmp273 + tmp275;
	       tmp296 = tmp273 - tmp275;
	       tmp309 = tmp306 - tmp308;
	       tmp323 = tmp306 + tmp308;
	  }
	  {
	       fftw_real tmp314;
	       fftw_real tmp318;
	       fftw_real tmp317;
	       fftw_real tmp315;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp281;
		    fftw_real tmp282;
		    fftw_real tmp289;
		    fftw_real tmp290;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp281 = X[iostride];
		    tmp282 = X[7 * iostride];
		    tmp283 = tmp281 + tmp282;
		    tmp314 = tmp281 - tmp282;
		    tmp289 = Y[-iostride];
		    tmp290 = Y[-7 * iostride];
		    tmp291 = tmp289 - tmp290;
		    tmp318 = tmp289 + tmp290;
	       }
	       {
		    fftw_real tmp284;
		    fftw_real tmp285;
		    fftw_real tmp292;
		    fftw_real tmp293;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp284 = X[3 * iostride];
		    tmp285 = X[5 * iostride];
		    tmp286 = tmp284 + tmp285;
		    tmp317 = tmp285 - tmp284;
		    tmp292 = Y[-3 * iostride];
		    tmp293 = Y[-5 * iostride];
		    tmp294 = tmp292 - tmp293;
		    tmp315 = tmp293 + tmp292;
	       }
	       tmp301 = tmp283 - tmp286;
	       tmp319 = tmp317 + tmp318;
	       tmp327 = tmp318 - tmp317;
	       tmp326 = tmp314 + tmp315;
	       tmp316 = tmp314 - tmp315;
	       tmp302 = tmp294 + tmp291;
	  }
	  {
	       fftw_real tmp280;
	       fftw_real tmp287;
	       fftw_real tmp288;
	       fftw_real tmp295;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp280 = tmp276 + tmp279;
	       tmp287 = K2_000000000 * (tmp283 + tmp286);
	       X[8 * iostride] = tmp280 - tmp287;
	       X[0] = tmp280 + tmp287;
	       tmp288 = tmp276 - tmp279;
	       tmp295 = K2_000000000 * (tmp291 - tmp294);
	       X[12 * iostride] = tmp288 + tmp295;
	       X[4 * iostride] = tmp288 - tmp295;
	  }
	  {
	       fftw_real tmp300;
	       fftw_real tmp303;
	       fftw_real tmp304;
	       fftw_real tmp305;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp300 = tmp296 - tmp299;
	       tmp303 = K1_414213562 * (tmp301 - tmp302);
	       X[10 * iostride] = tmp300 - tmp303;
	       X[2 * iostride] = tmp300 + tmp303;
	       tmp304 = tmp296 + tmp299;
	       tmp305 = K1_414213562 * (tmp301 + tmp302);
	       X[6 * iostride] = tmp304 - tmp305;
	       X[14 * iostride] = tmp304 + tmp305;
	  }
	  {
	       fftw_real tmp313;
	       fftw_real tmp320;
	       fftw_real tmp321;
	       fftw_real tmp322;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp313 = tmp309 + tmp312;
	       tmp320 = (K1_847759065 * tmp316) - (K765366864 * tmp319);
	       X[9 * iostride] = tmp313 - tmp320;
	       X[iostride] = tmp313 + tmp320;
	       tmp321 = tmp309 - tmp312;
	       tmp322 = (K765366864 * tmp316) + (K1_847759065 * tmp319);
	       X[5 * iostride] = tmp321 - tmp322;
	       X[13 * iostride] = tmp321 + tmp322;
	  }
	  {
	       fftw_real tmp325;
	       fftw_real tmp328;
	       fftw_real tmp329;
	       fftw_real tmp330;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp325 = tmp323 - tmp324;
	       tmp328 = (K765366864 * tmp326) - (K1_847759065 * tmp327);
	       X[11 * iostride] = tmp325 - tmp328;
	       X[3 * iostride] = tmp325 + tmp328;
	       tmp329 = tmp323 + tmp324;
	       tmp330 = (K1_847759065 * tmp326) + (K765366864 * tmp327);
	       X[7 * iostride] = tmp329 - tmp330;
	       X[15 * iostride] = tmp329 + tmp330;
	  }
     }
     X = X + dist;
     Y = Y - dist;
     for (i = 2; i < m; i = i + 2, X = X + dist, Y = Y - dist, W = W + 15) {
	  fftw_real tmp73;
	  fftw_real tmp98;
	  fftw_real tmp135;
	  fftw_real tmp160;
	  fftw_real tmp182;
	  fftw_real tmp236;
	  fftw_real tmp210;
	  fftw_real tmp248;
	  fftw_real tmp95;
	  fftw_real tmp124;
	  fftw_real tmp138;
	  fftw_real tmp164;
	  fftw_real tmp197;
	  fftw_real tmp216;
	  fftw_real tmp244;
	  fftw_real tmp252;
	  fftw_real tmp80;
	  fftw_real tmp128;
	  fftw_real tmp105;
	  fftw_real tmp161;
	  fftw_real tmp213;
	  fftw_real tmp237;
	  fftw_real tmp189;
	  fftw_real tmp249;
	  fftw_real tmp88;
	  fftw_real tmp115;
	  fftw_real tmp137;
	  fftw_real tmp163;
	  fftw_real tmp204;
	  fftw_real tmp215;
	  fftw_real tmp241;
	  fftw_real tmp251;
	  ASSERT_ALIGNED_DOUBLE;
	  {
	       fftw_real tmp69;
	       fftw_real tmp180;
	       fftw_real tmp131;
	       fftw_real tmp209;
	       fftw_real tmp72;
	       fftw_real tmp208;
	       fftw_real tmp134;
	       fftw_real tmp181;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp67;
		    fftw_real tmp68;
		    fftw_real tmp129;
		    fftw_real tmp130;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp67 = X[0];
		    tmp68 = Y[-8 * iostride];
		    tmp69 = tmp67 + tmp68;
		    tmp180 = tmp67 - tmp68;
		    tmp129 = Y[0];
		    tmp130 = X[8 * iostride];
		    tmp131 = tmp129 - tmp130;
		    tmp209 = tmp129 + tmp130;
	       }
	       {
		    fftw_real tmp70;
		    fftw_real tmp71;
		    fftw_real tmp132;
		    fftw_real tmp133;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp70 = X[4 * iostride];
		    tmp71 = Y[-12 * iostride];
		    tmp72 = tmp70 + tmp71;
		    tmp208 = tmp70 - tmp71;
		    tmp132 = Y[-4 * iostride];
		    tmp133 = X[12 * iostride];
		    tmp134 = tmp132 - tmp133;
		    tmp181 = tmp132 + tmp133;
	       }
	       tmp73 = tmp69 + tmp72;
	       tmp98 = tmp69 - tmp72;
	       tmp135 = tmp131 - tmp134;
	       tmp160 = tmp131 + tmp134;
	       tmp182 = tmp180 - tmp181;
	       tmp236 = tmp180 + tmp181;
	       tmp210 = tmp208 + tmp209;
	       tmp248 = tmp209 - tmp208;
	  }
	  {
	       fftw_real tmp91;
	       fftw_real tmp194;
	       fftw_real tmp119;
	       fftw_real tmp192;
	       fftw_real tmp94;
	       fftw_real tmp191;
	       fftw_real tmp122;
	       fftw_real tmp195;
	       fftw_real tmp116;
	       fftw_real tmp123;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp89;
		    fftw_real tmp90;
		    fftw_real tmp117;
		    fftw_real tmp118;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp89 = Y[-15 * iostride];
		    tmp90 = X[7 * iostride];
		    tmp91 = tmp89 + tmp90;
		    tmp194 = tmp89 - tmp90;
		    tmp117 = Y[-7 * iostride];
		    tmp118 = X[15 * iostride];
		    tmp119 = tmp117 - tmp118;
		    tmp192 = tmp117 + tmp118;
	       }
	       {
		    fftw_real tmp92;
		    fftw_real tmp93;
		    fftw_real tmp120;
		    fftw_real tmp121;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp92 = X[3 * iostride];
		    tmp93 = Y[-11 * iostride];
		    tmp94 = tmp92 + tmp93;
		    tmp191 = tmp92 - tmp93;
		    tmp120 = Y[-3 * iostride];
		    tmp121 = X[11 * iostride];
		    tmp122 = tmp120 - tmp121;
		    tmp195 = tmp120 + tmp121;
	       }
	       tmp95 = tmp91 + tmp94;
	       tmp116 = tmp91 - tmp94;
	       tmp123 = tmp119 - tmp122;
	       tmp124 = tmp116 + tmp123;
	       tmp138 = tmp123 - tmp116;
	       tmp164 = tmp119 + tmp122;
	       {
		    fftw_real tmp193;
		    fftw_real tmp196;
		    fftw_real tmp242;
		    fftw_real tmp243;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp193 = tmp191 - tmp192;
		    tmp196 = tmp194 - tmp195;
		    tmp197 = (K923879532 * tmp193) - (K382683432 * tmp196);
		    tmp216 = (K382683432 * tmp193) + (K923879532 * tmp196);
		    tmp242 = tmp194 + tmp195;
		    tmp243 = tmp191 + tmp192;
		    tmp244 = (K382683432 * tmp242) - (K923879532 * tmp243);
		    tmp252 = (K382683432 * tmp243) + (K923879532 * tmp242);
	       }
	  }
	  {
	       fftw_real tmp76;
	       fftw_real tmp183;
	       fftw_real tmp104;
	       fftw_real tmp184;
	       fftw_real tmp79;
	       fftw_real tmp186;
	       fftw_real tmp101;
	       fftw_real tmp187;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp74;
		    fftw_real tmp75;
		    fftw_real tmp102;
		    fftw_real tmp103;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp74 = X[2 * iostride];
		    tmp75 = Y[-10 * iostride];
		    tmp76 = tmp74 + tmp75;
		    tmp183 = tmp74 - tmp75;
		    tmp102 = Y[-2 * iostride];
		    tmp103 = X[10 * iostride];
		    tmp104 = tmp102 - tmp103;
		    tmp184 = tmp102 + tmp103;
	       }
	       {
		    fftw_real tmp77;
		    fftw_real tmp78;
		    fftw_real tmp99;
		    fftw_real tmp100;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp77 = Y[-14 * iostride];
		    tmp78 = X[6 * iostride];
		    tmp79 = tmp77 + tmp78;
		    tmp186 = tmp77 - tmp78;
		    tmp99 = Y[-6 * iostride];
		    tmp100 = X[14 * iostride];
		    tmp101 = tmp99 - tmp100;
		    tmp187 = tmp99 + tmp100;
	       }
	       tmp80 = tmp76 + tmp79;
	       tmp128 = tmp76 - tmp79;
	       tmp105 = tmp101 - tmp104;
	       tmp161 = tmp104 + tmp101;
	       {
		    fftw_real tmp211;
		    fftw_real tmp212;
		    fftw_real tmp185;
		    fftw_real tmp188;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp211 = tmp183 + tmp184;
		    tmp212 = tmp186 + tmp187;
		    tmp213 = K707106781 * (tmp211 - tmp212);
		    tmp237 = K707106781 * (tmp211 + tmp212);
		    tmp185 = tmp183 - tmp184;
		    tmp188 = tmp186 - tmp187;
		    tmp189 = K707106781 * (tmp185 + tmp188);
		    tmp249 = K707106781 * (tmp185 - tmp188);
	       }
	  }
	  {
	       fftw_real tmp84;
	       fftw_real tmp201;
	       fftw_real tmp110;
	       fftw_real tmp199;
	       fftw_real tmp87;
	       fftw_real tmp198;
	       fftw_real tmp113;
	       fftw_real tmp202;
	       fftw_real tmp107;
	       fftw_real tmp114;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp82;
		    fftw_real tmp83;
		    fftw_real tmp108;
		    fftw_real tmp109;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp82 = X[iostride];
		    tmp83 = Y[-9 * iostride];
		    tmp84 = tmp82 + tmp83;
		    tmp201 = tmp82 - tmp83;
		    tmp108 = Y[-iostride];
		    tmp109 = X[9 * iostride];
		    tmp110 = tmp108 - tmp109;
		    tmp199 = tmp108 + tmp109;
	       }
	       {
		    fftw_real tmp85;
		    fftw_real tmp86;
		    fftw_real tmp111;
		    fftw_real tmp112;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp85 = X[5 * iostride];
		    tmp86 = Y[-13 * iostride];
		    tmp87 = tmp85 + tmp86;
		    tmp198 = tmp85 - tmp86;
		    tmp111 = Y[-5 * iostride];
		    tmp112 = X[13 * iostride];
		    tmp113 = tmp111 - tmp112;
		    tmp202 = tmp111 + tmp112;
	       }
	       tmp88 = tmp84 + tmp87;
	       tmp107 = tmp84 - tmp87;
	       tmp114 = tmp110 - tmp113;
	       tmp115 = tmp107 - tmp114;
	       tmp137 = tmp107 + tmp114;
	       tmp163 = tmp110 + tmp113;
	       {
		    fftw_real tmp200;
		    fftw_real tmp203;
		    fftw_real tmp239;
		    fftw_real tmp240;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp200 = tmp198 + tmp199;
		    tmp203 = tmp201 - tmp202;
		    tmp204 = (K923879532 * tmp200) + (K382683432 * tmp203);
		    tmp215 = (K923879532 * tmp203) - (K382683432 * tmp200);
		    tmp239 = tmp201 + tmp202;
		    tmp240 = tmp199 - tmp198;
		    tmp241 = (K382683432 * tmp239) - (K923879532 * tmp240);
		    tmp251 = (K382683432 * tmp240) + (K923879532 * tmp239);
	       }
	  }
	  {
	       fftw_real tmp81;
	       fftw_real tmp96;
	       fftw_real tmp158;
	       fftw_real tmp162;
	       fftw_real tmp165;
	       fftw_real tmp166;
	       fftw_real tmp157;
	       fftw_real tmp159;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp81 = tmp73 + tmp80;
	       tmp96 = tmp88 + tmp95;
	       tmp158 = tmp81 - tmp96;
	       tmp162 = tmp160 + tmp161;
	       tmp165 = tmp163 + tmp164;
	       tmp166 = tmp162 - tmp165;
	       X[0] = tmp81 + tmp96;
	       Y[-15 * iostride] = tmp162 + tmp165;
	       tmp157 = c_re(W[7]);
	       tmp159 = c_im(W[7]);
	       X[8 * iostride] = (tmp157 * tmp158) + (tmp159 * tmp166);
	       Y[-7 * iostride] = (tmp157 * tmp166) - (tmp159 * tmp158);
	  }
	  {
	       fftw_real tmp170;
	       fftw_real tmp176;
	       fftw_real tmp174;
	       fftw_real tmp178;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp168;
		    fftw_real tmp169;
		    fftw_real tmp172;
		    fftw_real tmp173;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp168 = tmp160 - tmp161;
		    tmp169 = tmp88 - tmp95;
		    tmp170 = tmp168 - tmp169;
		    tmp176 = tmp169 + tmp168;
		    tmp172 = tmp73 - tmp80;
		    tmp173 = tmp164 - tmp163;
		    tmp174 = tmp172 - tmp173;
		    tmp178 = tmp172 + tmp173;
	       }
	       {
		    fftw_real tmp167;
		    fftw_real tmp171;
		    fftw_real tmp175;
		    fftw_real tmp177;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp167 = c_re(W[11]);
		    tmp171 = c_im(W[11]);
		    Y[-3 * iostride] = (tmp167 * tmp170) - (tmp171 * tmp174);
		    X[12 * iostride] = (tmp171 * tmp170) + (tmp167 * tmp174);
		    tmp175 = c_re(W[3]);
		    tmp177 = c_im(W[3]);
		    Y[-11 * iostride] = (tmp175 * tmp176) - (tmp177 * tmp178);
		    X[4 * iostride] = (tmp177 * tmp176) + (tmp175 * tmp178);
	       }
	  }
	  {
	       fftw_real tmp126;
	       fftw_real tmp142;
	       fftw_real tmp140;
	       fftw_real tmp144;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp106;
		    fftw_real tmp125;
		    fftw_real tmp136;
		    fftw_real tmp139;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp106 = tmp98 + tmp105;
		    tmp125 = K707106781 * (tmp115 + tmp124);
		    tmp126 = tmp106 - tmp125;
		    tmp142 = tmp106 + tmp125;
		    tmp136 = tmp128 + tmp135;
		    tmp139 = K707106781 * (tmp137 + tmp138);
		    tmp140 = tmp136 - tmp139;
		    tmp144 = tmp136 + tmp139;
	       }
	       {
		    fftw_real tmp97;
		    fftw_real tmp127;
		    fftw_real tmp141;
		    fftw_real tmp143;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp97 = c_re(W[9]);
		    tmp127 = c_im(W[9]);
		    X[10 * iostride] = (tmp97 * tmp126) + (tmp127 * tmp140);
		    Y[-5 * iostride] = (tmp97 * tmp140) - (tmp127 * tmp126);
		    tmp141 = c_re(W[1]);
		    tmp143 = c_im(W[1]);
		    X[2 * iostride] = (tmp141 * tmp142) + (tmp143 * tmp144);
		    Y[-13 * iostride] = (tmp141 * tmp144) - (tmp143 * tmp142);
	       }
	  }
	  {
	       fftw_real tmp148;
	       fftw_real tmp154;
	       fftw_real tmp152;
	       fftw_real tmp156;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp146;
		    fftw_real tmp147;
		    fftw_real tmp150;
		    fftw_real tmp151;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp146 = tmp135 - tmp128;
		    tmp147 = K707106781 * (tmp115 - tmp124);
		    tmp148 = tmp146 - tmp147;
		    tmp154 = tmp146 + tmp147;
		    tmp150 = tmp98 - tmp105;
		    tmp151 = K707106781 * (tmp138 - tmp137);
		    tmp152 = tmp150 - tmp151;
		    tmp156 = tmp150 + tmp151;
	       }
	       {
		    fftw_real tmp145;
		    fftw_real tmp149;
		    fftw_real tmp153;
		    fftw_real tmp155;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp145 = c_re(W[13]);
		    tmp149 = c_im(W[13]);
		    Y[-iostride] = (tmp145 * tmp148) - (tmp149 * tmp152);
		    X[14 * iostride] = (tmp149 * tmp148) + (tmp145 * tmp152);
		    tmp153 = c_re(W[5]);
		    tmp155 = c_im(W[5]);
		    Y[-9 * iostride] = (tmp153 * tmp154) - (tmp155 * tmp156);
		    X[6 * iostride] = (tmp155 * tmp154) + (tmp153 * tmp156);
	       }
	  }
	  {
	       fftw_real tmp206;
	       fftw_real tmp220;
	       fftw_real tmp218;
	       fftw_real tmp222;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp190;
		    fftw_real tmp205;
		    fftw_real tmp214;
		    fftw_real tmp217;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp190 = tmp182 - tmp189;
		    tmp205 = tmp197 - tmp204;
		    tmp206 = tmp190 - tmp205;
		    tmp220 = tmp190 + tmp205;
		    tmp214 = tmp210 - tmp213;
		    tmp217 = tmp215 - tmp216;
		    tmp218 = tmp214 - tmp217;
		    tmp222 = tmp214 + tmp217;
	       }
	       {
		    fftw_real tmp179;
		    fftw_real tmp207;
		    fftw_real tmp219;
		    fftw_real tmp221;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp179 = c_re(W[12]);
		    tmp207 = c_im(W[12]);
		    X[13 * iostride] = (tmp179 * tmp206) + (tmp207 * tmp218);
		    Y[-2 * iostride] = (tmp179 * tmp218) - (tmp207 * tmp206);
		    tmp219 = c_re(W[4]);
		    tmp221 = c_im(W[4]);
		    X[5 * iostride] = (tmp219 * tmp220) + (tmp221 * tmp222);
		    Y[-10 * iostride] = (tmp219 * tmp222) - (tmp221 * tmp220);
	       }
	  }
	  {
	       fftw_real tmp226;
	       fftw_real tmp232;
	       fftw_real tmp230;
	       fftw_real tmp234;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp224;
		    fftw_real tmp225;
		    fftw_real tmp228;
		    fftw_real tmp229;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp224 = tmp210 + tmp213;
		    tmp225 = tmp204 + tmp197;
		    tmp226 = tmp224 - tmp225;
		    tmp232 = tmp224 + tmp225;
		    tmp228 = tmp182 + tmp189;
		    tmp229 = tmp215 + tmp216;
		    tmp230 = tmp228 - tmp229;
		    tmp234 = tmp228 + tmp229;
	       }
	       {
		    fftw_real tmp223;
		    fftw_real tmp227;
		    fftw_real tmp231;
		    fftw_real tmp233;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp223 = c_re(W[8]);
		    tmp227 = c_im(W[8]);
		    Y[-6 * iostride] = (tmp223 * tmp226) - (tmp227 * tmp230);
		    X[9 * iostride] = (tmp227 * tmp226) + (tmp223 * tmp230);
		    tmp231 = c_re(W[0]);
		    tmp233 = c_im(W[0]);
		    Y[-14 * iostride] = (tmp231 * tmp232) - (tmp233 * tmp234);
		    X[iostride] = (tmp233 * tmp232) + (tmp231 * tmp234);
	       }
	  }
	  {
	       fftw_real tmp246;
	       fftw_real tmp256;
	       fftw_real tmp254;
	       fftw_real tmp258;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp238;
		    fftw_real tmp245;
		    fftw_real tmp250;
		    fftw_real tmp253;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp238 = tmp236 - tmp237;
		    tmp245 = tmp241 + tmp244;
		    tmp246 = tmp238 - tmp245;
		    tmp256 = tmp238 + tmp245;
		    tmp250 = tmp248 + tmp249;
		    tmp253 = tmp251 - tmp252;
		    tmp254 = tmp250 - tmp253;
		    tmp258 = tmp250 + tmp253;
	       }
	       {
		    fftw_real tmp235;
		    fftw_real tmp247;
		    fftw_real tmp255;
		    fftw_real tmp257;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp235 = c_re(W[10]);
		    tmp247 = c_im(W[10]);
		    X[11 * iostride] = (tmp235 * tmp246) + (tmp247 * tmp254);
		    Y[-4 * iostride] = (tmp235 * tmp254) - (tmp247 * tmp246);
		    tmp255 = c_re(W[2]);
		    tmp257 = c_im(W[2]);
		    X[3 * iostride] = (tmp255 * tmp256) + (tmp257 * tmp258);
		    Y[-12 * iostride] = (tmp255 * tmp258) - (tmp257 * tmp256);
	       }
	  }
	  {
	       fftw_real tmp262;
	       fftw_real tmp268;
	       fftw_real tmp266;
	       fftw_real tmp270;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp260;
		    fftw_real tmp261;
		    fftw_real tmp264;
		    fftw_real tmp265;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp260 = tmp248 - tmp249;
		    tmp261 = tmp241 - tmp244;
		    tmp262 = tmp260 + tmp261;
		    tmp268 = tmp260 - tmp261;
		    tmp264 = tmp236 + tmp237;
		    tmp265 = tmp251 + tmp252;
		    tmp266 = tmp264 - tmp265;
		    tmp270 = tmp264 + tmp265;
	       }
	       {
		    fftw_real tmp259;
		    fftw_real tmp263;
		    fftw_real tmp267;
		    fftw_real tmp269;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp259 = c_re(W[6]);
		    tmp263 = c_im(W[6]);
		    Y[-8 * iostride] = (tmp259 * tmp262) - (tmp263 * tmp266);
		    X[7 * iostride] = (tmp263 * tmp262) + (tmp259 * tmp266);
		    tmp267 = c_re(W[14]);
		    tmp269 = c_im(W[14]);
		    Y[0] = (tmp267 * tmp268) - (tmp269 * tmp270);
		    X[15 * iostride] = (tmp269 * tmp268) + (tmp267 * tmp270);
	       }
	  }
     }
     if (i == m) {
	  fftw_real tmp7;
	  fftw_real tmp51;
	  fftw_real tmp19;
	  fftw_real tmp43;
	  fftw_real tmp39;
	  fftw_real tmp47;
	  fftw_real tmp59;
	  fftw_real tmp64;
	  fftw_real tmp14;
	  fftw_real tmp56;
	  fftw_real tmp24;
	  fftw_real tmp32;
	  fftw_real tmp29;
	  fftw_real tmp33;
	  fftw_real tmp54;
	  fftw_real tmp65;
	  fftw_real tmp63;
	  fftw_real tmp66;
	  ASSERT_ALIGNED_DOUBLE;
	  {
	       fftw_real tmp3;
	       fftw_real tmp15;
	       fftw_real tmp38;
	       fftw_real tmp57;
	       fftw_real tmp6;
	       fftw_real tmp35;
	       fftw_real tmp18;
	       fftw_real tmp58;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp1;
		    fftw_real tmp2;
		    fftw_real tmp36;
		    fftw_real tmp37;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp1 = X[0];
		    tmp2 = X[7 * iostride];
		    tmp3 = tmp1 + tmp2;
		    tmp15 = tmp1 - tmp2;
		    tmp36 = Y[0];
		    tmp37 = Y[-7 * iostride];
		    tmp38 = tmp36 + tmp37;
		    tmp57 = tmp36 - tmp37;
	       }
	       {
		    fftw_real tmp4;
		    fftw_real tmp5;
		    fftw_real tmp16;
		    fftw_real tmp17;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp4 = X[4 * iostride];
		    tmp5 = X[3 * iostride];
		    tmp6 = tmp4 + tmp5;
		    tmp35 = tmp4 - tmp5;
		    tmp16 = Y[-4 * iostride];
		    tmp17 = Y[-3 * iostride];
		    tmp18 = tmp16 + tmp17;
		    tmp58 = tmp16 - tmp17;
	       }
	       tmp7 = tmp3 + tmp6;
	       tmp51 = tmp3 - tmp6;
	       tmp19 = tmp15 - tmp18;
	       tmp43 = tmp15 + tmp18;
	       tmp39 = tmp35 + tmp38;
	       tmp47 = tmp38 - tmp35;
	       tmp59 = tmp57 - tmp58;
	       tmp64 = tmp58 + tmp57;
	  }
	  {
	       fftw_real tmp10;
	       fftw_real tmp20;
	       fftw_real tmp23;
	       fftw_real tmp53;
	       fftw_real tmp13;
	       fftw_real tmp25;
	       fftw_real tmp28;
	       fftw_real tmp52;
	       ASSERT_ALIGNED_DOUBLE;
	       {
		    fftw_real tmp8;
		    fftw_real tmp9;
		    fftw_real tmp21;
		    fftw_real tmp22;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp8 = X[2 * iostride];
		    tmp9 = X[5 * iostride];
		    tmp10 = tmp8 + tmp9;
		    tmp20 = tmp8 - tmp9;
		    tmp21 = Y[-2 * iostride];
		    tmp22 = Y[-5 * iostride];
		    tmp23 = tmp21 + tmp22;
		    tmp53 = tmp21 - tmp22;
	       }
	       {
		    fftw_real tmp11;
		    fftw_real tmp12;
		    fftw_real tmp26;
		    fftw_real tmp27;
		    ASSERT_ALIGNED_DOUBLE;
		    tmp11 = X[iostride];
		    tmp12 = X[6 * iostride];
		    tmp13 = tmp11 + tmp12;
		    tmp25 = tmp11 - tmp12;
		    tmp26 = Y[-iostride];
		    tmp27 = Y[-6 * iostride];
		    tmp28 = tmp26 + tmp27;
		    tmp52 = tmp27 - tmp26;
	       }
	       tmp14 = tmp10 + tmp13;
	       tmp56 = tmp10 - tmp13;
	       tmp24 = tmp20 - tmp23;
	       tmp32 = tmp20 + tmp23;
	       tmp29 = tmp25 - tmp28;
	       tmp33 = tmp25 + tmp28;
	       tmp54 = tmp52 - tmp53;
	       tmp65 = tmp53 + tmp52;
	  }
	  X[0] = K2_000000000 * (tmp7 + tmp14);
	  X[8 * iostride] = -(K2_000000000 * (tmp65 + tmp64));
	  tmp63 = tmp7 - tmp14;
	  tmp66 = tmp64 - tmp65;
	  X[4 * iostride] = K1_414213562 * (tmp63 - tmp66);
	  X[12 * iostride] = -(K1_414213562 * (tmp63 + tmp66));
	  {
	       fftw_real tmp61;
	       fftw_real tmp62;
	       fftw_real tmp55;
	       fftw_real tmp60;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp61 = tmp51 - tmp54;
	       tmp62 = tmp59 - tmp56;
	       X[6 * iostride] = (K765366864 * tmp61) - (K1_847759065 * tmp62);
	       X[14 * iostride] = -((K1_847759065 * tmp61) + (K765366864 * tmp62));
	       tmp55 = tmp51 + tmp54;
	       tmp60 = tmp56 + tmp59;
	       X[2 * iostride] = (K1_847759065 * tmp55) - (K765366864 * tmp60);
	       X[10 * iostride] = -((K765366864 * tmp55) + (K1_847759065 * tmp60));
	  }
	  {
	       fftw_real tmp45;
	       fftw_real tmp49;
	       fftw_real tmp48;
	       fftw_real tmp50;
	       fftw_real tmp44;
	       fftw_real tmp46;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp44 = K707106781 * (tmp32 + tmp33);
	       tmp45 = tmp43 - tmp44;
	       tmp49 = tmp43 + tmp44;
	       tmp46 = K707106781 * (tmp24 - tmp29);
	       tmp48 = tmp46 + tmp47;
	       tmp50 = tmp47 - tmp46;
	       X[3 * iostride] = (K1_662939224 * tmp45) - (K1_111140466 * tmp48);
	       X[11 * iostride] = -((K1_111140466 * tmp45) + (K1_662939224 * tmp48));
	       X[7 * iostride] = (K390180644 * tmp49) - (K1_961570560 * tmp50);
	       X[15 * iostride] = -((K1_961570560 * tmp49) + (K390180644 * tmp50));
	  }
	  {
	       fftw_real tmp31;
	       fftw_real tmp41;
	       fftw_real tmp40;
	       fftw_real tmp42;
	       fftw_real tmp30;
	       fftw_real tmp34;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp30 = K707106781 * (tmp24 + tmp29);
	       tmp31 = tmp19 + tmp30;
	       tmp41 = tmp19 - tmp30;
	       tmp34 = K707106781 * (tmp32 - tmp33);
	       tmp40 = tmp34 + tmp39;
	       tmp42 = tmp39 - tmp34;
	       X[iostride] = (K1_961570560 * tmp31) - (K390180644 * tmp40);
	       X[9 * iostride] = -((K390180644 * tmp31) + (K1_961570560 * tmp40));
	       X[5 * iostride] = (K1_111140466 * tmp41) - (K1_662939224 * tmp42);
	       X[13 * iostride] = -((K1_662939224 * tmp41) + (K1_111140466 * tmp42));
	  }
     }
}

static const int twiddle_order[] =
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
fftw_codelet_desc fftw_hc2hc_backward_16_desc =
{
     "fftw_hc2hc_backward_16",
     (void (*)()) fftw_hc2hc_backward_16,
     16,
     FFTW_BACKWARD,
     FFTW_HC2HC,
     366,
     15,
     twiddle_order,
};


/* im_invfft
 *
 * Copyright: 1990, N. Dessipris.
 *
 * Author: Nicos Dessipris
 * Written on: 12/04/1990
 * Modified on :
 * 28/6/95 JC
 *	- rewritten, based on new im_fwfft() code
 * 10/9/98 JC
 *	- frees memory more quickly
 * 2/4/02 JC
 *	- fftw code added
 * 13/7/02 JC
 *	- Type reset
 * 27/2/03 JC
 *	- tiny speed-up ... save 1 copy on write
 * 22/1/04 JC
 *	- oops, fix for segv on wider than high fftw transforms
 * 3/11/04
 *	- added fftw3 support
 * 7/2/10
 * 	- gtkdoc
 */

/*

    This file is part of VIPS.

    VIPS is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser 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

 */

/*

    These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>

#include <stdio.h>
#include <math.h>

#ifdef HAVE_FFTW
#include <fftw.h>
#endif /*HAVE_FFTW*/

#ifdef HAVE_FFTW3
#include <fftw3.h>
#endif /*HAVE_FFTW3*/

#include <vips/vips.h>
#include <vips/internal.h>

#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/

#ifdef HAVE_FFTW
/* Call fftw for a 1 band image.
 */
static int
invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out )
{
	fftwnd_plan plan;

	IMAGE *cmplx = im_open_local( out, "invfft1:1", "t" );

	/* Make dp complex image.
	 */
	if( !cmplx || im_pincheck( in ) || im_poutcheck( out ) )
		return( -1 );
	if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) {
                im_error( "im_invfft", "%s", _( "one band uncoded only" ) );
                return( -1 );
	}
	if( im_clip2fmt( in, cmplx, IM_BANDFMT_DPCOMPLEX ) )
                return( -1 );

	/* Make the plan for the transform. Yes, they really do use nx for
	 * height and ny for width.
	 */
	if( !(plan = fftw2d_create_plan( in->Ysize, in->Xsize,
		FFTW_BACKWARD,
		FFTW_MEASURE | FFTW_USE_WISDOM | FFTW_IN_PLACE )) ) {
                im_error( "im_invfft",
			"%s", _( "unable to create transform plan" ) );
		return( -1 );
	}

	fftwnd_one( plan, (fftw_complex *) cmplx->data, NULL );

	fftwnd_destroy_plan( plan );

	/* Copy to out.
	 */
        if( im_copy( cmplx, out ) )
                return( -1 );

	return( 0 );
}
#else /*!HAVE_FFTW*/
#ifdef HAVE_FFTW3
/* Complex to complex inverse transform.
 */
static int
invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out )
{
	fftw_plan plan;

	IMAGE *cmplx = im_open_local( out, "invfft1:1", "t" );

	/* We have to have a separate buffer for the planner to work on.
	 */
	double *planner_scratch = IM_ARRAY( dummy,
		in->Xsize * in->Ysize * 2, double );

	/* Make dp complex image.
	 */
	if( !cmplx || im_pincheck( in ) || im_poutcheck( out ) )
		return( -1 );
	if( in->Coding != IM_CODING_NONE || in->Bands != 1 ) {
                im_error( "im_invfft",
			"%s", _( "one band uncoded only" ) );
                return( -1 );
	}
	if( im_clip2fmt( in, cmplx, IM_BANDFMT_DPCOMPLEX ) )
                return( -1 );

	/* Make the plan for the transform. Yes, they really do use nx for
	 * height and ny for width.
	 */
	if( !(plan = fftw_plan_dft_2d( in->Ysize, in->Xsize,
		(fftw_complex *) planner_scratch,
		(fftw_complex *) planner_scratch,
		FFTW_BACKWARD,
		0 )) ) {
                im_error( "im_invfft",
			"%s", _( "unable to create transform plan" ) );
		return( -1 );
	}

	fftw_execute_dft( plan,
		(fftw_complex *) cmplx->data, (fftw_complex *) cmplx->data );

	fftw_destroy_plan( plan );

	/* Copy to out.
	 */
        if( im_copy( cmplx, out ) )
                return( -1 );

	return( 0 );
}
#else /*!HAVE_FFTW3*/
/* Fall back to VIPS's built-in fft
 */
static int
invfft1( IMAGE *dummy, IMAGE *in, IMAGE *out )
{
	int bpx = im_ispoweroftwo( in->Xsize );
	int bpy = im_ispoweroftwo( in->Ysize );
	float *buf, *q, *p1, *p2;
	int x, y;

	/* Buffers for real and imaginary parts.
	 */
	IMAGE *real = im_open_local( dummy, "invfft1:1", "t" );
	IMAGE *imag = im_open_local( dummy, "invfft1:2", "t" );

	/* Temps.
	 */
	IMAGE *t1 = im_open_local( dummy, "invfft1:3", "p" );
	IMAGE *t2 = im_open_local( dummy, "invfft1:4", "p" );

	if( !real || !imag || !t1 )
		return( -1 );
        if( im_pincheck( in ) || im_outcheck( out ) )
                return( -1 );
        if( in->Coding != IM_CODING_NONE ||
		in->Bands != 1 || !im_iscomplex( in ) ) {
                im_error( "im_invfft",
			"%s", _( "one band complex uncoded only" ) );
                return( -1 );
	}
	if( !bpx || !bpy ) {
		im_error( "im_invfft",
			"%s", _( "sides must be power of 2" ) );
		return( -1 );
	}

	/* Make sure we have a single-precision complex input image.
	 */
	if( im_clip2fmt( in, t1, IM_BANDFMT_COMPLEX ) )
		return( -1 );

	/* Extract real and imag parts. We have to complement the imaginary.
	 */
	if( im_c2real( t1, real ) )
		return( -1 );
	if( im_c2imag( t1, t2 ) || im_lintra( -1.0, t2, 0.0, imag ) )
		return( -1 );

	/* Transform!
	 */
	if( im__fft_sp( (float *) real->data, (float *) imag->data,
		bpx - 1, bpy - 1 ) ) {
                im_error( "im_invfft",
			"%s", _( "fft_sp failed" ) );
                return( -1 );
	}

	/* WIO to out.
	 */
        if( im_cp_desc( out, in ) )
                return( -1 );
	out->BandFmt = IM_BANDFMT_COMPLEX;
        if( im_setupout( out ) )
                return( -1 );
	if( !(buf = (float *) IM_ARRAY( dummy,
		IM_IMAGE_SIZEOF_LINE( out ), PEL )) )
		return( -1 );

	/* Gather together real and imag parts.
	 */
	for( p1 = (float *) real->data, p2 = (float *) imag->data,
		y = 0; y < out->Ysize; y++ ) {
		q = buf;

		for( x = 0; x < out->Xsize; x++ ) {
			q[0] = *p1++;
			q[1] = *p2++;
			q += 2;
		}

		if( im_writeline( y, out, (PEL *) buf ) )
			return( -1 );
	}

	return( 0 );
}
#endif /*HAVE_FFTW3*/
#endif /*HAVE_FFTW*/

/**
 * im_invfft:
 * @in: input image
 * @out: output image
 *
 * Transform an image from Fourier space to real space. The result is complex.
 * If you are OK with a real result, use im_invfftr() instead, it's quicker.
 *
 * VIPS uses the fftw3 or fftw2 Fourier transform libraries if possible. If
 * they were not available when VIPS was built, it falls back to it's own
 * FFT functions which are slow and only work for square images whose sides
 * are a power of two.
 *
 * See also: im_invfftr(), im_fwfft(), im_disp_ps().
 *
 * Returns: 0 on success, -1 on error.
 */
int
im_invfft( IMAGE *in, IMAGE *out )
{
	IMAGE *dummy = im_open( "im_invfft:1", "p" );

	if( !dummy )
		return( -1 );
	if( im__fftproc( dummy, in, out, invfft1 ) ) {
		im_close( dummy );
		return( -1 );
	}
	im_close( dummy );

	if( out->Bands == 1 )
		out->Type = IM_TYPE_B_W;
	else
		out->Type = IM_TYPE_MULTIBAND;

	return( 0 );
}


/*
 Copyright (C) 2002 M. Marques, A. Castro, A. Rubio, G. Bertsch

 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA
 02110-1301, USA.

 $Id: parse_exp.c 13079 2015-02-18 16:06:42Z dstrubbe $
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>

#include "liboct_parser.h"
#include "symbols.h"

static char *par_string;
static int par_pos;
parse_result par_res;

static int oct_parser_lex(void);
static int oct_parser_error (char *s)  /* Called by oct_parser_parse on error */
{
  /* Do nothing */
  /* printf("%s\n", s); */
  return 0;
}

/* include the parser */
#include "grammar.c"

int parse_exp(char *exp, parse_result *r)
{
  int o;

  par_string = exp;
  par_pos = 0;

  o = oct_parser_parse();
  if(o == 0){
    r->type = par_res.type;
    if(r->type == PR_CMPLX)
      r->value.c = par_res.value.c;
    else
      r->value.s = par_res.value.s;
  }
  return o;
}

int get_real(char *s, double *d)
{
  int n=0;
  sscanf(s, "%lg", d);
  while(*s && (isdigit(*s) || *s=='.' || *s=='e' || *s=='E')){
    if((*s=='e' || *s=='E') && (*(s+1)=='+' || *(s+1)=='-')) {s++; n++;}
    s++; n++;
  }
  return n;
}

static int oct_parser_lex (){
  int c;
  char *symbuf = 0, *symbuf2 = 0;
  int length = 0;

  /* Ignore whitespace, get first nonwhite character.  */
  while ((c = par_string[par_pos++]) == ' ' || c == '\t');

  if (c == '\0')
    return '\n';

  /* Char starts a number => parse the number.         */
  if (c == '.' || isdigit (c)){
    par_pos--;
    par_pos += get_real(&par_string[par_pos], &GSL_REAL(yylval.val));
    return NUM;
  }

  /* get the logical operators */
  if (c == '<' && par_string[par_pos] == '='){
    par_pos++;
    return LE;
  }

  if (c == '>' && par_string[par_pos] == '='){
    par_pos++;
    return GE;
  }

  if (c == '=' && par_string[par_pos] == '='){
    par_pos++;
    return EQUAL;
  }

  /*
  if (c == ':' && par_string[par_pos] == '='){
    par_pos++;
    return SET;
  }
  */

  if (c == '&' && par_string[par_pos] == '&'){
    par_pos++;
    return LAND;
  }

  if (c == '|' && par_string[par_pos] == '|'){
    par_pos++;
    return LOR;
  }

  /* Char starts an identifier => read the name.       */
  if (isalpha (c) || c == '\'' || c == '\"'){
    symrec *s;
    char startc = (char)c;
    int i;

    /* Initially make the buffer long enough
       for a 40-character symbol name.  */
    if (length == 0)
      length = 40, symbuf = (char *)malloc (length + 1);

    if(startc == '\'' || startc == '\"')
      c = par_string[par_pos++];
    else
      startc = 0; /* false */

    i = 0;
    do{
      /* If buffer is full, make it larger.        */
      if (i == length){
	length *= 2;
	symbuf2 = (char *)realloc (symbuf, length + 1);
	if (symbuf2 == NULL) {
	  fprintf(stderr, "Parser error: failed to reallocate buffer to size %i.\n", length);
	  exit(1);
	} else {
	  symbuf = symbuf2;
	}
      }
      /* Add this character to the buffer.         */
      symbuf[i++] = (char)c;
      /* Get another character.                    */
      c = par_string[par_pos++];
    }while (c != '\0' &&
	    ((startc && c!=startc) || (!startc && (isalnum(c) || c == '_' ))));

    if(!startc) par_pos--;
    symbuf[i] = '\0';

    if(!startc){
      s = getsym (symbuf);
      if (s == 0){
	int jj;
	for (jj = 0; reserved_symbols[jj] != 0; jj++){
	  if(strcmp(symbuf, reserved_symbols[jj]) == 0){
	    fprintf(stderr, "Parser error: trying to redefine reserved symbol '%s'.\n", symbuf);
	    exit(1);
	  }
	}

	s = putsym (symbuf, S_CMPLX);
      }
      yylval.tptr = s;

      free(symbuf);
      if(s->type == S_CMPLX)
	return VAR;
      else
	return FNCT;
    }else{
      yylval.str = strdup(symbuf);

      free(symbuf);
      return STR;
    }
  }

  /* Any other character is a token by itself.        */
  return c;
}


/*
Copyright (c) 2015, Patrick Weltevrede
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_sf.h>
#include "psrsalsa.h"
#define AmoebaAlgorithm 0
int readVonMisesModel(char *filename, vonMises_collection_definition *components, verbose_definition verbose)
{
  int i;
  FILE *fin;
  if(verbose.verbose) printf("Opening %s for reading\n", filename);
  fin = fopen(filename, "r");
  if(fin == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readVonMisesModel: Cannot open %s", filename);
    return 0;
  }
  for(components->nrcomponents = 0; components->nrcomponents < maxNrVonMisesComponents; (components->nrcomponents)++) {
    i = fscanf(fin, "%lf %lf %lf", &(components->centre[components->nrcomponents]), &(components->concentration[components->nrcomponents]), &(components->height[components->nrcomponents]));
    if(i != 3) {
      break;
    }else if(verbose.verbose) {
      printf("  component %d: phase=%e concentration=%e amplitude=%e\n", components->nrcomponents, components->centre[components->nrcomponents], components->concentration[components->nrcomponents], components->height[components->nrcomponents]);
    }
  }
  if(verbose.verbose) printf("Closing %s\n", filename);
  fclose(fin);
  if(components->nrcomponents == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readVonMisesModel: No components found in %s", filename);
    return 0;
  }
  return 1;
}
double calcVonMisesFunction2(double centre, double concentration, double height, double phase, double shift)
{
  double y;
  y = exp((cos(2.0*M_PI*(phase-centre-shift))-1.0)*concentration) * height;
  return y;
}
double integrateVonMisesFunction2(double concentration, double height)
{
  double area;
  area = 2*M_PI*height*gsl_sf_bessel_I0_scaled(concentration);
  return area;
}
double widthVonMisesFunction2(double concentration, double ampfrac)
{
  double value = log(ampfrac)/concentration + 1.0;
  if(value < -1.0 || value >= 1.0) {
    return sqrt(-1);
  }
  return 2.0*acos(value);
}
double calcVonMisesFunction(vonMises_collection_definition *components, double phase, double shift)
{
  int n;
  double y;
  y = 0;
  if(components->nrcomponents > 0) {
    for(n = 0; n < components->nrcomponents; n++) {
      y += calcVonMisesFunction2(components->centre[n], components->concentration[n], components->height[n], phase, shift);
    }
  }
  return y;
}
void calcVonMisesProfile(vonMises_collection_definition *components, int nrbins, float *profile, double shift, int normalize)
{
  int i;
  double x, Imax = -1;
  for(i = 0; i < nrbins; i++) {
    profile[i] = 0;
  }
  for(i = 0; i < nrbins; i++) {
    x = i/(double)nrbins;
    profile[i] = calcVonMisesFunction(components, x, shift);
  }
  if(normalize) {
    for(i = 0; i < nrbins; i++) {
      if(profile[i] > Imax)
 Imax = profile[i];
    }
    for(i = 0; i < nrbins; i++) {
      profile[i] /= Imax;
    }
  }
}
float correlateVonMisesFunction(vonMises_collection_definition *components, int nrbins, float *profile, verbose_definition verbose)
{
  float correl_max, *profile2;
  int i;
  int ishift;
  if(verbose.verbose) {
    for(i = 0; i < verbose.indent; i++)
      printf(" ");
    printf("Correlating template with profile\n");
    verbose.indent += 2;
  }
  profile2 = (float *)malloc(nrbins*sizeof(float));
  if(profile2 == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR correlateVonMisesFunction: Memory allocation error.");
    return 0;
  }
  calcVonMisesProfile(components, nrbins, profile2, 0, 0);
  find_peak_correlation(profile, profile2, nrbins, 0, 1, 1, 1, &ishift, &correl_max, verbose);
  if(verbose.verbose) {
    for(i = 0; i < verbose.indent; i++)
      printf(" ");
    printf("Found a shift of %d bins (%f phase) and max/min correlation is %f.\n", ishift, ishift/(float)nrbins, correl_max);
    for(i = 0; i < verbose.indent; i++)
      printf(" ");
    printf("so you could do a shift by %d bins (%f phase) to rotate profile to align with model.\n", -ishift, -ishift/(float)nrbins);
  }
  free(profile2);
  return ishift/(float)nrbins;
}


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

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

#ifdef VRNA_WITH_GSL
#include <gsl/gsl_multimin.h>
#endif

#include "ViennaRNA/eval.h"
#include "ViennaRNA/fold_vars.h"
#include "ViennaRNA/constraints/hard.h"
#include "ViennaRNA/constraints/soft.h"
#include "ViennaRNA/fold.h"
#include "ViennaRNA/part_func.h"
#include "ViennaRNA/utils/basic.h"
#include "ViennaRNA/params/basic.h"
#include "ViennaRNA/perturbation_fold.h"

static void
calculate_probability_unpaired(vrna_fold_compound_t *vc,
                               double               *probability)
{
  int         length = vc->length;
  FLT_OR_DBL  *probs = vc->exp_matrices->probs;
  int         *iidx = vc->iindx;
  int         i, j;

  for (i = 0; i <= length; ++i)
    probability[i] = 1;

  for (i = 1; i <= length; ++i)
    for (j = i + 1; j <= length; ++j) {
      probability[i]  -= probs[iidx[i] - j];
      probability[j]  -= probs[iidx[i] - j];
    }
}

#if 0
static double
calculate_norm(double *vector,
               int    length)
{
  double  sum = 0;
  int     i;

  for (i = 1; i <= length; ++i)
    sum += vector[i] * vector[i];

  return sqrt(sum);
}

#endif

static void
addSoftConstraint(vrna_fold_compound_t  *vc,
                  const double          *epsilon,
                  int                   length)
{
  /* remove previous soft constraints */
  vrna_sc_init(vc);

  /* prepare vector of unpaired constraints in kcal/mol */
  FLT_OR_DBL *constraints = (FLT_OR_DBL *)vrna_alloc(sizeof(FLT_OR_DBL) * (length + 1));

  memcpy(constraints + 1, epsilon + 1, sizeof(FLT_OR_DBL) * length);

  /* add new soft constraints */
  vrna_sc_set_up(vc, (const FLT_OR_DBL *)constraints, VRNA_OPTION_DEFAULT);

  free(constraints);
}

static double
evaluate_objective_function_contribution(double value,
                                         int    objective_function)
{
  if (objective_function == VRNA_OBJECTIVE_FUNCTION_QUADRATIC)
    return value * value;

  if (objective_function == VRNA_OBJECTIVE_FUNCTION_ABSOLUTE)
    return fabs(value);

  assert(0);
  return 0;
}

static double
evaluate_perturbation_vector_score(vrna_fold_compound_t *vc,
                                   const double         *epsilon,
                                   const double         *q_prob_unpaired,
                                   double               sigma_squared,
                                   double               tau_squared,
                                   int                  objective_function)
{
  double  ret   = 0;
  double  ret2  = 0.;
  double  *p_prob_unpaired;
  int     i;
  int     length = vc->length;

  /* calculate pairing probabilty in the pertubated energy model */
  p_prob_unpaired = vrna_alloc(sizeof(double) * (length + 1));

  addSoftConstraint(vc, epsilon, length);

  vc->params->model_details.compute_bpp     = 1;
  vc->exp_params->model_details.compute_bpp = 1;

  /* get new (constrained) MFE to scale pf computations properly */
  double mfe = (double)vrna_mfe(vc, NULL);

  vrna_exp_params_rescale(vc, &mfe);

  vrna_pf(vc, NULL);

  calculate_probability_unpaired(vc, p_prob_unpaired);

  vrna_sc_remove(vc);

  for (i = 1; i <= length; ++i) {
    /* add penalty for pertubation energies */
    ret += evaluate_objective_function_contribution(epsilon[i], objective_function) / tau_squared;

    /* add penalty for mismatches between observed and predicted probabilities */
    if (q_prob_unpaired[i] >= 0) /* ignore positions with missing data */
      ret2 += evaluate_objective_function_contribution(p_prob_unpaired[i] - q_prob_unpaired[i],
                                                       objective_function) / sigma_squared;
  }

  vrna_message_info(stderr, "Score: pertubation: %g\tdiscrepancy: %g", ret, ret2);
  free(p_prob_unpaired);

  return ret + ret2;
}

static void
pairing_probabilities_from_restricted_pf(vrna_fold_compound_t *vc,
                                         const double         *epsilon,
                                         double               *prob_unpaired,
                                         double               **conditional_prob_unpaired)
{
  int length = vc->length;
  int i;

  addSoftConstraint(vc, epsilon, length);
  vc->params->model_details.compute_bpp     = 1;
  vc->exp_params->model_details.compute_bpp = 1;

  /* get new (constrained) MFE to scale pf computations properly */
  double mfe = (double)vrna_mfe(vc, NULL);

  vrna_exp_params_rescale(vc, &mfe);

  vrna_pf(vc, NULL);

  calculate_probability_unpaired(vc, prob_unpaired);

#ifdef _OPENMP
#pragma omp parallel for private(i)
#endif
  for (i = 1; i <= length; ++i) {
    vrna_fold_compound_t  *restricted_vc;
    char                  *hc_string;
    unsigned int          constraint_options = VRNA_CONSTRAINT_DB
                                               | VRNA_CONSTRAINT_DB_PIPE
                                               | VRNA_CONSTRAINT_DB_DOT
                                               | VRNA_CONSTRAINT_DB_X
                                               | VRNA_CONSTRAINT_DB_ANG_BRACK
                                               | VRNA_CONSTRAINT_DB_RND_BRACK;

    hc_string = vrna_alloc(sizeof(char) * (length + 1));
    memset(hc_string, '.', length);
    hc_string[i - 1] = 'x';

    restricted_vc = vrna_fold_compound(vc->sequence,
                                       &(vc->exp_params->model_details),
                                       VRNA_OPTION_DEFAULT);
    vrna_constraints_add(restricted_vc, hc_string, constraint_options);
    free(hc_string);

    vrna_exp_params_subst(restricted_vc, vc->exp_params);

    vrna_pf(restricted_vc, NULL);
    calculate_probability_unpaired(restricted_vc, conditional_prob_unpaired[i]);

    restricted_vc->sc = NULL;
    vrna_fold_compound_free(restricted_vc);
  }

  vrna_sc_remove(vc);
}

static void
pairing_probabilities_from_sampling(vrna_fold_compound_t  *vc,
                                    const double          *epsilon,
                                    int                   sample_size,
                                    double                *prob_unpaired,
                                    double                **conditional_prob_unpaired,
                                    unsigned int          options)
{
  char    **samples, **ptr;
  int     length, i, j;
  double  mfe;

  length = vc->length;
  addSoftConstraint(vc, epsilon, length);

  vc->params->model_details.compute_bpp     = 0;
  vc->exp_params->model_details.compute_bpp = 0;

  /* get new (constrained) MFE to scale pf computations properly */
  mfe = (double)vrna_mfe(vc, NULL);
  vrna_exp_params_rescale(vc, &mfe);

  vrna_pf(vc, NULL);

  samples = vrna_pbacktrack_num(vc,
                                (unsigned int)sample_size,
                                options);

  for (ptr = samples; (*ptr); ptr++) {
    for (i = length; i > 0; i--) {
      if ((*ptr)[i - 1] == '.') {
        ++prob_unpaired[i];

        for (j = length; j > 0; j--)
          if ((*ptr)[j - 1] == '.')
            ++conditional_prob_unpaired[i][j];
      }
    }

    free(*ptr);
  }

  free(samples);

  for (i = 1; i <= length; ++i) {
    if (prob_unpaired[i])
      for (j = 1; j <= length; ++j)
        conditional_prob_unpaired[i][j] /= prob_unpaired[i];

    prob_unpaired[i] /= sample_size;

    assert(prob_unpaired[i] >= 0 && prob_unpaired[i] <= 1);
  }

  vrna_sc_remove(vc);
}

static void
allocateProbabilityArrays(double  **unpaired,
                          double  ***conditional_unpaired,
                          int     length)
{
  int i;

  *unpaired             = vrna_alloc(sizeof(double) * (length + 1));
  *conditional_unpaired = vrna_alloc(sizeof(double *) * (length + 1));

  for (i = 1; i <= length; ++i)
    (*conditional_unpaired)[i] = vrna_alloc(sizeof(double) * (length + 1));
}

static void
freeProbabilityArrays(double  *unpaired,
                      double  **conditional_unpaired,
                      int     length)
{
  int i;

  free(unpaired);
  for (i = 1; i <= length; ++i)
    free(conditional_unpaired[i]);
  free(conditional_unpaired);
}

static void
evaluate_perturbation_vector_gradient(vrna_fold_compound_t  *vc,
                                      const double          *epsilon,
                                      const double          *q_prob_unpaired,
                                      double                sigma_squared,
                                      double                tau_squared,
                                      int                   objective_function,
                                      int                   sample_size,
                                      double                *gradient)
{
  double  *p_prob_unpaired;
  double  **p_conditional_prob_unpaired;
  int     i, mu;
  int     length  = vc->length;
  double  kT      = vc->exp_params->kT / 1000;

  allocateProbabilityArrays(&p_prob_unpaired, &p_conditional_prob_unpaired, length);

  if (sample_size > 0) {
    pairing_probabilities_from_sampling(vc,
                                        epsilon,
                                        sample_size,
                                        p_prob_unpaired,
                                        p_conditional_prob_unpaired,
                                        VRNA_PBACKTRACK_DEFAULT);
  } else if (sample_size < 0) {
    pairing_probabilities_from_sampling(vc,
                                        epsilon,
                                        -sample_size,
                                        p_prob_unpaired,
                                        p_conditional_prob_unpaired,
                                        VRNA_PBACKTRACK_NON_REDUNDANT);
  } else {
    pairing_probabilities_from_restricted_pf(vc,
                                             epsilon,
                                             p_prob_unpaired,
                                             p_conditional_prob_unpaired);
  }

  for (mu = 1; mu <= length; ++mu) {
    double sum = 0;

    if (objective_function == VRNA_OBJECTIVE_FUNCTION_QUADRATIC) {
      for (i = 1; i <= length; ++i) {
        if (q_prob_unpaired[i] < 0) /* ignore positions with missing data */
          continue;

        sum += (p_prob_unpaired[i] - q_prob_unpaired[i])
               * p_prob_unpaired[i] * (p_prob_unpaired[mu] - p_conditional_prob_unpaired[i][mu])
               / sigma_squared;
      }

      gradient[mu] = 2 * (epsilon[mu] / tau_squared + sum / kT);
    } else if (objective_function == VRNA_OBJECTIVE_FUNCTION_ABSOLUTE) {
      for (i = 1; i <= length; ++i)
        if (q_prob_unpaired[i] >= 0 && p_prob_unpaired[i] != q_prob_unpaired[i]) {
          sum += (p_prob_unpaired[i] * (p_prob_unpaired[mu] - p_conditional_prob_unpaired[i][mu])) /
                 kT
                 / sigma_squared
                 * (p_prob_unpaired[i] > q_prob_unpaired[i] ? 1. : -1.);
        }

      if (epsilon[mu])
        sum += (epsilon[mu] > 0 ? 1. : -1.) / tau_squared;

      gradient[mu] = sum;
    }
  }

  freeProbabilityArrays(p_prob_unpaired, p_conditional_prob_unpaired, length);
}

#ifdef VRNA_WITH_GSL
typedef struct parameters_gsl {
  vrna_fold_compound_t  *vc;
  const double          *q_prob_unpaired;
  double                sigma_squared;
  double                tau_squared;
  int                   objective_function;
  int                   sample_size;
} parameters_gsl;

static double
f_gsl(const gsl_vector  *x,
      void              *params)
{
  parameters_gsl *p = params;

  return evaluate_perturbation_vector_score(p->vc,
                                            x->data,
                                            p->q_prob_unpaired,
                                            p->sigma_squared,
                                            p->tau_squared,
                                            p->objective_function);
}

static void
df_gsl(const gsl_vector *x,
       void             *params,
       gsl_vector       *df)
{
  parameters_gsl *p = params;

  gsl_vector_set(df, 0, 0);
  evaluate_perturbation_vector_gradient(p->vc,
                                        x->data,
                                        p->q_prob_unpaired,
                                        p->sigma_squared,
                                        p->tau_squared,
                                        p->objective_function,
                                        p->sample_size,
                                        df->data);
}

static void
fdf_gsl(const gsl_vector  *x,
        void              *params,
        double            *f,
        gsl_vector        *g)
{
  *f = f_gsl(x, params);
  df_gsl(x, params, g);
}

#endif /* VRNA_WITH_GSL */

PUBLIC void
vrna_sc_minimize_pertubation(vrna_fold_compound_t *vc,
                             const double         *q_prob_unpaired,
                             int                  objective_function,
                             double               sigma_squared,
                             double               tau_squared,
                             int                  algorithm,
                             int                  sample_size,
                             double               *epsilon,
                             double               initialStepSize,
                             double               minStepSize,
                             double               minImprovement,
                             double               minimizerTolerance,
                             progress_callback    callback)
{
  int                                   iteration_count = 0;
  const int                             max_iterations  = 100;
  int                                   length          = vc->length;

#ifdef VRNA_WITH_GSL
  const gsl_multimin_fdfminimizer_type  *minimizer_type = 0;

  struct {
    int                                   type;
    const gsl_multimin_fdfminimizer_type  *gsl_type;
  } algorithms[] =
  { { VRNA_MINIMIZER_CONJUGATE_FR,
      gsl_multimin_fdfminimizer_conjugate_fr },
    { VRNA_MINIMIZER_CONJUGATE_PR,
      gsl_multimin_fdfminimizer_conjugate_pr },
    { VRNA_MINIMIZER_VECTOR_BFGS,
      gsl_multimin_fdfminimizer_vector_bfgs },
    { VRNA_MINIMIZER_VECTOR_BFGS2,
      gsl_multimin_fdfminimizer_vector_bfgs2 },
    { VRNA_MINIMIZER_STEEPEST_DESCENT,
      gsl_multimin_fdfminimizer_steepest_descent },
    { 0,
      NULL } };
  int i;
  for (i = 0; algorithms[i].type; ++i)
    if (algorithms[i].type == algorithm) {
      minimizer_type = algorithms[i].gsl_type;
      break;
    }

  if (minimizer_type) {
    parameters_gsl            parameters;
    gsl_multimin_function_fdf fdf;
    gsl_multimin_fdfminimizer *minimizer;
    gsl_vector                *vector;

    int                       status;

    parameters.vc                 = vc;
    parameters.q_prob_unpaired    = q_prob_unpaired;
    parameters.sigma_squared      = sigma_squared;
    parameters.tau_squared        = tau_squared;
    parameters.objective_function = objective_function;
    parameters.sample_size        = sample_size;

    fdf.n       = length + 1;
    fdf.f       = &f_gsl;
    fdf.df      = &df_gsl;
    fdf.fdf     = &fdf_gsl;
    fdf.params  = (void *)&parameters;

    minimizer = gsl_multimin_fdfminimizer_alloc(minimizer_type, length + 1);
    vector    = gsl_vector_calloc(length + 1);

    /* gsl_multimin_fdfminimizer_set(minimizer, &fdf, vector, 0.01, 1e-4); */
    gsl_multimin_fdfminimizer_set(minimizer, &fdf, vector, initialStepSize, minimizerTolerance);

    if (callback)
      callback(0, minimizer->f, minimizer->x->data);

    do {
      ++iteration_count;
      status = gsl_multimin_fdfminimizer_iterate(minimizer);

      if (callback)
        callback(iteration_count, minimizer->f, minimizer->x->data);

      if (status)
        break;

      status = gsl_multimin_test_gradient(minimizer->gradient, minimizerTolerance);
    } while (status == GSL_CONTINUE && iteration_count < max_iterations);

    memcpy(epsilon, minimizer->x->data, sizeof(double) * (length + 1));

    gsl_multimin_fdfminimizer_free(minimizer);
    gsl_vector_free(vector);

    return;
  }

#endif /* VRNA_WITH_GSL */

  double        improvement;
  const double  min_improvement = minImprovement;

  double        *new_epsilon  = vrna_alloc(sizeof(double) * (length + 1));
  double        *gradient     = vrna_alloc(sizeof(double) * (length + 1));

  double        score = evaluate_perturbation_vector_score(vc,
                                                           epsilon,
                                                           q_prob_unpaired,
                                                           sigma_squared,
                                                           tau_squared,
                                                           objective_function);

  if (callback)
    callback(0, score, epsilon);

  do {
    double  new_score;
    double  step_size;

    ++iteration_count;

    evaluate_perturbation_vector_gradient(vc,
                                          epsilon,
                                          q_prob_unpaired,
                                          sigma_squared,
                                          tau_squared,
                                          objective_function,
                                          sample_size,
                                          gradient);

    /*    step_size = 0.5 / calculate_norm(gradient, length);*/
    step_size = initialStepSize;

    do {
      int i;
      for (i = 1; i <= length; ++i)
        new_epsilon[i] = epsilon[i] - step_size * gradient[i];

      new_score = evaluate_perturbation_vector_score(vc,
                                                     new_epsilon,
                                                     q_prob_unpaired,
                                                     sigma_squared,
                                                     tau_squared,
                                                     objective_function);
      improvement = 1 - new_score / score;
      step_size   /= 2;
    } while ((improvement < min_improvement) && (step_size >= minStepSize));

    if (new_score > score)
      break;

    if (callback)
      callback(iteration_count, new_score, new_epsilon);

    score = new_score;
    memcpy(epsilon, new_epsilon, sizeof(double) * (length + 1));
  } while (improvement >= min_improvement && iteration_count < max_iterations);

  free(gradient);
  free(new_epsilon);
}


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

#include <gsl/gsl_integration.h>
#include <gsl/gsl_interp.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_errno.h>

#include "ccl.h"

/*
 * Spline the linear power spectrum for mu-Sigma MG cosmologies.
 * @param cosmo Cosmological parameters
 ^ @param psp The linear power spectrum to spline.
 * @param status, integer indicating the status
 */
void ccl_cosmology_spline_linpower_musigma(ccl_cosmology* cosmo, ccl_f2d_t *psp, int rescaled_mg_flag, int* status) {
  double kmin, kmax, ndecades, amin, amax, ic, sigma8, log_sigma8;
  int nk, na, s;
  double *lk = NULL, *aa = NULL, *lpk_ln = NULL, *lpk_nl = NULL;
  double norm_pk;
  double *mnu_list = NULL;
  ccl_parameters params_GR;
  params_GR.m_nu = NULL;
  params_GR.z_mgrowth = NULL;
  params_GR.df_mgrowth = NULL;
  ccl_cosmology * cosmo_GR = NULL;
  double *D_mu = NULL;
  double *D_GR = NULL;

  if (*status == 0) {
    //calculations done - now allocate CCL splines
    kmin = 2*exp(psp->lkmin);
    kmax = cosmo->spline_params.K_MAX_SPLINE;
    //Compute nk from number of decades and N_K = # k per decade
    ndecades = log10(kmax) - log10(kmin);
    nk = (int)ceil(ndecades*cosmo->spline_params.N_K);
    amin = cosmo->spline_params.A_SPLINE_MINLOG_PK;
    amax = cosmo->spline_params.A_SPLINE_MAX;
    na = cosmo->spline_params.A_SPLINE_NA_PK+cosmo->spline_params.A_SPLINE_NLOG_PK-1;

    // The lk array is initially k, but will later
    // be overwritten with log(k)
    lk = ccl_log_spacing(kmin, kmax, nk);
    if (lk == NULL) {
      *status = CCL_ERROR_MEMORY;
      ccl_cosmology_set_status_message(
          cosmo,
          "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): memory allocation\n");
    }
  }

  if (*status == 0) {
    aa = ccl_linlog_spacing(
        amin, cosmo->spline_params.A_SPLINE_MIN_PK,
        amax, cosmo->spline_params.A_SPLINE_NLOG_PK,
        cosmo->spline_params.A_SPLINE_NA_PK);
    if (aa == NULL) {
      *status = CCL_ERROR_MEMORY;
      ccl_cosmology_set_status_message(
        cosmo,
        "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): memory allocation\n");
    }
  }

  if (*status == 0) {
    lpk_ln = malloc(nk * na * sizeof(double));
    if (lpk_ln == NULL) {
      *status = CCL_ERROR_MEMORY;
      ccl_cosmology_set_status_message(
        cosmo,
        "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): memory allocation\n");
    }
  }

  if (*status == 0) {
    lpk_nl = malloc(nk * na * sizeof(double));
    if(lpk_nl == NULL) {
      *status = CCL_ERROR_MEMORY;
      ccl_cosmology_set_status_message(
        cosmo,
        "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): memory allocation\n");
    }
  }

  if (*status == 0) {
    // After this loop lk will contain log(k),
    // lpk_ln will contain log(P_lin), all in Mpc, not Mpc/h units!
    double psout_l;
    s = 0;

    // If scale-independent mu / Sigma modified gravity is in use
    // and mu ! = 0 : get the unnormalized growth factor in MG and for
    // corresponding GR case, to rescale CLASS power spectrum
    if (fabs(cosmo->params.mu_0) > 1e-14) {
      // Set up another cosmology which is exactly the same as the
      // current one but with mu_0 and Sigma_0=0, for scaling P(k)

      // Get a list of the three neutrino masses already calculated
      mnu_list = malloc(3*sizeof(double));
      if (mnu_list == NULL) {
        *status = CCL_ERROR_MEMORY;
        ccl_cosmology_set_status_message(
          cosmo,
          "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): memory allocation\n");
      }

      if (*status == 0) {
        for (int i=0; i< cosmo->params.N_nu_mass; i=i+1) {
          mnu_list[i] = cosmo->params.m_nu[i];
        }
        if (cosmo->params.N_nu_mass < 3) {
          for (int j=cosmo->params.N_nu_mass; j<3; j=j+1) {
            mnu_list[j] = 0.;
          }
        }

        if (isfinite(cosmo->params.A_s)) {
          norm_pk = cosmo->params.A_s;
        }
        else if (isfinite(cosmo->params.sigma8)) {
          norm_pk = cosmo->params.sigma8;
        }
        else {
          *status = CCL_ERROR_PARAMETERS;
          strcpy(
            cosmo->status_message,
            "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): neither A_s nor sigma8 defined.\n");
        }
      }

      if (*status == 0) {
        params_GR = ccl_parameters_create(
          cosmo->params.Omega_c, cosmo->params.Omega_b, cosmo->params.Omega_k,
          cosmo->params.Neff, mnu_list, cosmo->params.N_nu_mass,
          cosmo->params.w0, cosmo->params.wa, cosmo->params.h,
          norm_pk, cosmo->params.n_s,
          cosmo->params.bcm_log10Mc, cosmo->params.bcm_etab,
          cosmo->params.bcm_ks, 0., 0., cosmo->params.nz_mgrowth,
          cosmo->params.z_mgrowth, cosmo->params.df_mgrowth, status);

        if (*status) {
          *status = CCL_ERROR_PARAMETERS;
          strcpy(
            cosmo->status_message,
            "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): could not make MG params.\n");
        }
      }

      if (*status == 0) {
        cosmo_GR = ccl_cosmology_create(params_GR, cosmo->config);
        D_mu = malloc(na * sizeof(double));
        D_GR = malloc(na * sizeof(double));

        if (cosmo_GR == NULL || D_mu == NULL || D_GR == NULL) {
          *status = CCL_ERROR_MEMORY;
          ccl_cosmology_set_status_message(
            cosmo,
            "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): memory allocation\n");
        }
      }

      if (*status == 0) {
        ccl_cosmology_compute_growth(cosmo_GR, status);

        if (*status) {
          *status = CCL_ERROR_PARAMETERS;
          strcpy(
            cosmo->status_message,
            "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): could not init GR growth.\n");
        }
      }

      if (*status == 0) {
        for (int i=0; i<na; i++) {
          D_mu[i] = ccl_growth_factor_unnorm(cosmo, aa[i], status);
          D_GR[i] = ccl_growth_factor_unnorm(cosmo_GR, aa[i], status);
        }

        if (*status) {
          *status = CCL_ERROR_PARAMETERS;
          strcpy(
            cosmo->status_message,
            "ccl_power.c: ccl_cosmology_spline_linpower_musigma(): could not make MG and GR growth.\n");
        }
      }

      if (*status == 0) {
        for (int i=0; i<nk; i++) {
          lk[i] = log(lk[i]);
          for (int j = 0; j < na; j++) {
            //The 2D interpolation routines access the function values pk_{k_ia_j} with the following ordering:
            //pk_ij = pk[j*N_k + i]
            //with i = 0,...,N_k-1 and j = 0,...,N_a-1.
            psout_l = ccl_f2d_t_eval(psp, lk[i], aa[j], cosmo, status);
	    if (rescaled_mg_flag == 0) {
            lpk_ln[j*nk+i] = log(psout_l) ;
	    }
	    else {
            lpk_ln[j*nk+i] = log(psout_l) + 2 * log(D_mu[j]) - 2 * log(D_GR[j]);
	    }

          }
        }
      }
    }
    else {
      // This is the normal GR case.
      for (int i=0; i<nk; i++) {
        lk[i] = log(lk[i]);
        for (int j = 0; j<na; j++) {
          //The 2D interpolation routines access the function values pk_{k_ia_j} with the following ordering:
          //pk_ij = pk[j*N_k + i]
          //with i = 0,...,N_k-1 and j = 0,...,N_a-1.
          psout_l = ccl_f2d_t_eval(psp, lk[i], aa[j], cosmo, status);
          lpk_ln[j*nk+i] = log(psout_l);
        }
      }
    }
  }

  if (*status == 0) {
    cosmo->data.p_lin = ccl_f2d_t_new(
      na, aa, nk, lk, lpk_ln, NULL, NULL, 0,
      1, 2, ccl_f2d_cclgrowth, 1, NULL, 0, 2,
      ccl_f2d_3,status);
  }

  // if desried, renomalize to a given sigma8
  if (isfinite(cosmo->params.sigma8) && (!isfinite(cosmo->params.A_s))) {
    if (*status == 0) {
      cosmo->computed_linear_power = true;
      sigma8 = ccl_sigma8(cosmo, status);
      cosmo->computed_linear_power = false;
    }

    if (*status == 0) {
      // Calculate normalization factor using computed value of sigma8, then
      // recompute P(k, a) using this normalization
      log_sigma8 = 2*(log(cosmo->params.sigma8) - log(sigma8));
      for(int j = 0; j<na*nk; j++)
        lpk_ln[j] += log_sigma8;
    }

    if (*status == 0) {
      // Free the previous P(k,a) spline, and allocate a new one to store the
      // properly-normalized P(k,a)
      ccl_f2d_t_free(cosmo->data.p_lin);
      cosmo->data.p_lin = ccl_f2d_t_new(
        na, aa, nk, lk, lpk_ln, NULL, NULL, 0,
        1, 2, ccl_f2d_cclgrowth, 1, NULL, 0, 2,
        ccl_f2d_3,status);
    }
  }

  free(D_mu);
  free(D_GR);
  free(mnu_list);
  ccl_parameters_free(&params_GR);
  ccl_cosmology_free(cosmo_GR);
  free(lk);
  free(aa);
  free(lpk_nl);
  free(lpk_ln);
}


/*
 * Copyright 2020 Makani Technologies LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SIM_PHYSICS_WIND_H_
#define SIM_PHYSICS_WIND_H_

#include <gsl/gsl_vector.h>

#include <string>

#include "common/c_math/mat3.h"
#include "common/c_math/vec3.h"
#include "common/macros.h"

// Functions related to wind shear and turbulence.
//
// Many of these functions define a "mean wind frame" denoted by
// the "mw" subscript.  This should not be confused with the
// wind frame defined in sim/physics/aero_frame.h, which related to
// the apparent wind at a body.
//
// The origin of the mean wind frame lies at ground level directly
// below the ground station origin.  The axes are defined by:
//   x: Points along the predominate wind vector.
//   y: Completes a right-handed frame.
//   z: Points up.
// These are chosen to be consistent with NREL's TurbSim software
// and various IEC standards.

namespace sim {

namespace physics {

namespace wind {

double CalcWindShear(double height_agl, double ref_height_agl,
                     double ref_wind_speed, double wind_shear_exponent);

class FrozenTurbulenceWindDatabase {
  friend class FrozenTurbulenceWindDatabaseTest;

 public:
  FrozenTurbulenceWindDatabase(double t0, double y0,
                               const std::string &filename);
  ~FrozenTurbulenceWindDatabase() {
    gsl_vector_free(u_);
    gsl_vector_free(v_);
    gsl_vector_free(w_);
  }
  void CalcWind(double t, const Vec3 &pos_mw, Vec3 *wind_mw) const;

 private:
  // Time offset [s] to apply.  At t = 0, positions whose x coordinate
  // (in the mw frame) is zero will fetch the database entry given at
  // time t0_.
  double t0_;
  // Y offset [m] to apply from the center of the wind database.
  double y0_;
  // Mean wind speed [m/s] that sets the rate at which the database
  // is "advected" along the mean wind coordinate x axis.
  double mean_wind_speed_;
  // Number [#] of t, y, and z grid points.
  int32_t num_t_, num_y_, num_z_;
  // Duration [s], width [m], and height [m] of the grid defining
  // the database.
  double duration_, width_, height_;
  // Vectors storing the components of the wind velocity [m/s].  Each
  // vector has num_t * num_y * num_z elements.  The u, v, and w
  // components point along the x, y, and z axes of the mean wind
  // coordinate system.
  gsl_vector *u_;
  gsl_vector *v_;
  gsl_vector *w_;

  DISALLOW_COPY_AND_ASSIGN(FrozenTurbulenceWindDatabase);
};

enum class IecTurbulenceCategory { kA, kB, kC };

enum class IecWindTurbineClass { kI, kII, kIII };

class IecWindModel {
 public:
  IecWindModel(double hub_height_agl__, double rotor_diameter,
               double hub_wind_speed, IecTurbulenceCategory turbulence_category,
               IecWindTurbineClass wind_turbine_class);

  double hub_height_agl() const { return hub_height_agl_; }
  double ntm_sigma_1() const { return ntm_sigma_1_; }
  double ewm_sigma_1() const { return ewm_sigma_1_; }
  double etm_sigma_1() const { return etm_sigma_1_; }

  // IEC 61400-1 Design situations.

  // IEC 61400-1 Sec. 6.3.1.2 -- Normal Wind Profile Model (NWP).
  double CalcNormalWindProfile(double height_agl) const;

  // IEC 61400-1 Sec. 6.3.2.1 -- Extreme wind speed model (EWM).
  double CalcExtremeWindSpeed1YearRecurrence(double height_agl,
                                             bool is_turbulent) const;
  double CalcExtremeWindSpeed50YearRecurrence(double height_agl,
                                              bool is_turbulent) const;

  // IEC 61400-1 Sec. 6.3.2.2 -- Extreme Operating Gust (EOG).
  double CalcExtremeOperatingGust(double t, double height_agl) const;

  // IEC 61400-1 Sec. 6.3.2.4 -- Extreme direction change (EDC).
  double CalcExtremeDirectionChange(double t) const;

  // IEC 61400-1 Sec. 6.3.2.5 -- Extreme coherent gust with direction
  // change (ECD).
  double CalcExtremeCoherentGustWithDirectionChange(
      double t, double height_agl, double *wind_direction) const;

  // IEC 61400-1 Sec. 6.3.2.6 -- Extreme wind shear (EWS).
  double CalcExtremeWindShearVertical(double t, double height_agl) const;
  double CalcExtremeWindShearHorizontal(double t, double height_agl,
                                        double y) const;

 private:
  // Hub height above ground level [m].
  const double hub_height_agl_;

  // Rotor diameter [m].
  const double rotor_diameter_;

  // Nominal wind speed [m/s] at hub height.
  const double hub_wind_speed_;

  // Reference wind speed [m/s], see IEC 61400-1 Sec. 6.2.
  const double v_ref_;

  // Expected value of the turbulence intensity [#] at 15 m/s.
  const double I_ref_;

  // Longitudinal turbulence scale parameter [#].
  const double lambda_1_;

  // Normal turbulence model (NTM) standard deviation [m/s].
  const double ntm_sigma_1_;

  // Extreme wind model (EWM) standard deviation [m/s].
  const double ewm_sigma_1_;

  // Extreme turbulence model (ETM) standard deviation [m/s].
  const double etm_sigma_1_;
};

}  // namespace wind

}  // namespace physics

}  // namespace sim

#endif  // SIM_PHYSICS_WIND_H_


#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <float.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include "common.h"
#include "graph.h"
#include "utils.h"

void graph_init(graph *g, int vertices)
{
    g->num_v = vertices;
    g->num_e = (int*)malloc(vertices * sizeof(int));
    g->capacity = (int*)malloc(vertices * sizeof(int));
    g->adj_list = (int**)malloc(vertices * sizeof(int*));
    g->w_list = (double**)malloc(vertices * sizeof(double*));

    int i = 0;
    for (; i < vertices; ++i)
    {
        g->num_e[i] = 0;
        g->capacity[i] = GRAPH_INIT_CAPACITY;
        g->adj_list[i] = (int*)malloc(GRAPH_INIT_CAPACITY * sizeof(int));
        g->w_list[i] = (double*)malloc(GRAPH_INIT_CAPACITY * sizeof(double));
    }
}

ORIGIN_INLINE int graph_edges(const graph *g)
{
    const int num_v = g->num_v;
    int sum = 0;
    int v = 0;
    for (; v < num_v; ++v)
    {
        sum += g->num_e[v];
    }
    return sum;
}

ORIGIN_INLINE int graph_proper_edges(const graph *g)
{
    const int num_v = g->num_v;
    int sum = 0;
    int v = 0;
    for (; v < num_v; ++v)
    {
        int e = 0;
        for (; e < g->num_e[v]; ++e)
        {
            if (g->adj_list[v][e] != v)
            {
                ++sum;
            }
        }
    }
    return sum;
}

ORIGIN_INLINE int graph_loops(const graph *g)
{
    const int num_v = g->num_v;
    int sum = 0;
    int v = 0;
    for (; v < num_v; ++v)
    {
        int e = 0;
        for (; e < g->num_e[v]; ++e)
        {
            if (g->adj_list[v][e] == v)
            {
                ++sum;
            }
        }
    }
    return sum;
}

ORIGIN_INLINE int graph_outdegree(const graph *g, int u)
{
    return g->num_e[u];
}

ORIGIN_INLINE int graph_indegree(const graph *g, int u)
{
    const int num_v = g->num_v;
    int sum = 0;
    int v = 0;
    for (; v < num_v; ++v)
    {
        int e = 0;
        for (; e < g->num_e[v]; ++e)
        {
            if (g->adj_list[v][e] == u)
            {
                ++sum;
            }
        }
    }
    return sum;
}

ORIGIN_INLINE int graph_is_balanced(const graph *g)
{
    const int num_v = g->num_v;
    int u = 0;
    for (; u < num_v; ++u)
    {
        if (graph_indegree(g, u) != graph_outdegree(g, u))
        {
            return FALSE;
        }
    }
    return TRUE;
}

ORIGIN_INLINE void graph_add_edge(graph *g, int u, int v, double weight)
{
    if (g->num_e[u] == g->capacity[u])
    {
        graph_grow_lists(g, u);
    }
    g->adj_list[u][g->num_e[u]] = v;
    g->w_list[u][g->num_e[u]] = weight;
    g->num_e[u]++;
}

void graph_add_sym_edges(graph *g, int u, int v, double weight)
{
    graph_add_edge(g, u, v, weight);
    graph_add_edge(g, v, u, weight);
}

int graph_rmv_edge(graph *g, int u, int v)
{
    const int num_e = g->num_e[u];
    int e = 0;
    for (; e < num_e; ++e)
    {
        if (g->adj_list[u][e] == v)
        {
            g->num_e[u]--;
            int f = e;
            for (; f < num_e - 1; ++f)
            {
                g->adj_list[u][f] = g->adj_list[u][f+1];
                g->w_list[u][f] = g->w_list[u][f+1];
            }
            return TRUE;
        }
    }
    return FALSE;
}

int graph_rmv_sym_edges(graph *g, int u, int v)
{
    return (graph_rmv_edge(g, u, v) && graph_rmv_edge(g, v, u));
}

int graph_has_edge(graph *g, int u, int v)
{
    const int num_e = g->num_e[u];
    int e = 0;
    for (; e < num_e; ++e)
    {
        if (g->adj_list[u][e] == v)
        {
            return TRUE;
        }
    }
    return FALSE;
}

int graph_strongly_connected(const graph *g)
{
    const int num_v = g->num_v;
    int *group = (int*)malloc(num_v * sizeof(int));

    int u = 0;
    for (; u < num_v; ++u)
    {
        int v = 0;
        for (; v < num_v; ++v)
        {
            group[v] = FALSE;
        }

        group[u] = TRUE;
        graph_test_cc(g, group, u); // Call recursive function

        for (v = 0; v < num_v; ++v)
        {
            if (group[v] == FALSE)
            {
                free(group);
                return FALSE;
            }
        }
    }
    free(group);
    return TRUE;
}

void graph_svg(const graph *g, double *x, double *y, double size, double offset, FILE *out)
{
    fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">");
    const int num_v = g->num_v;
    for (int i = 0; i < num_v; ++i)
    {
        const int num_e = g->num_e[i];
        for (int j = 0; j < num_e; ++j)
        {
            const int e = g->adj_list[i][j];
            fprintf(out, "  <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:rgb(0,0,0);stroke-width:2\"/>\n", offset + x[i] * size, offset + y[i] * size, offset + x[e] * size, offset + y[e] * size);
        }
    }
    for (int i = 0; i < num_v; ++i)
    {
        fprintf(out, "  <circle id=\"%d\" cx=\"%f\" cy=\"%f\" r=\"8\" fill=\"rgb(0,0,0)\"/>\n", i, offset + x[i] * size, offset + y[i] * size);
    }
    fprintf(out, "</svg>");
}

void graph_svg_abun(const graph *g, double *x, double *y, double size, double offset, double *abun, int color, FILE *out)
{
    fprintf(out, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">");
    const int num_v = g->num_v;
    for (int i = 0; i < num_v; ++i)
    {
        const int num_e = g->num_e[i];
        for (int j = 0; j < num_e; ++j)
        {
            const int e = g->adj_list[i][j];
            fprintf(out, "  <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:rgb(0,0,0);stroke-width:2\"/>\n", offset + x[i] * size, offset + y[i] * size, offset + x[e] * size, offset + y[e] * size);
        }
    }
    int clr[3] = {0, 0, 0};
    for (int i = 0; i < num_v; ++i)
    {
        clr[color] = (int)(abun[i] * 255);
        fprintf(out, "  <circle id=\"%d\" cx=\"%f\" cy=\"%f\" r=\"8\" stroke=\"black\" stroke-width=\"1\" fill=\"rgb(%d,%d,%d)\"/>\n", i, offset + x[i] * size, offset + y[i] * size, clr[0], clr[1], clr[2]);
    }
    fprintf(out, "</svg>");
}

void graph_graphml(const graph *g, FILE *out, unsigned int id)
{
    const int num_v = g->num_v;
    fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
    fprintf(out, "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\"");
    fprintf(out, " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
    fprintf(out, " xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">\n");

    fprintf(out, "  <graph id=\"%u\" edgedefault=\"directed\">\n", id);
    for (int i = 0; i < num_v; ++i)
    {
        fprintf(out, "    <node id=\"v%d\"/>\n", i);
    }
    for (int i = 0; i < num_v; ++i)
    {
        for (int e = 0; e < g->num_e[i]; ++e)
        {
            fprintf(out, "    <edge source=\"v%d\" target=\"v%d\"/>\n", i, g->adj_list[i][e]);
        }
    }
    fprintf(out, "  </graph>\n");
    fprintf(out, "</graphml>\n");
}

void graph_print(const graph *g, FILE *out)
{
    for (int i = 0; i < g->num_v; ++i)
    {
        fprintf(out, "%5d -> ", i);
        for (int e = 0; e < g->num_e[i]; ++e)
        {
            fprintf(out, "%d ", g->adj_list[i][e]);
        }
        fprintf(out, "\n");
    }
}

void graph_free(graph *g)
{
    const int num_v = g->num_v;
    for (int i = 0; i < num_v; ++i)
    {
        free(g->adj_list[i]);
        free(g->w_list[i]);
    }
    free(g->adj_list);
    g->adj_list = NULL;
    free(g->w_list);
    g->w_list = NULL;
    free(g->num_e);
    g->num_e = NULL;
    free(g->capacity);
    g->capacity = NULL;
}

void graph_get_rgg(graph *g, int vertices, double r, double *x, double *y, gsl_rng *rng)
{
    graph_init(g, vertices);

    for (int i = 0; i < vertices; ++i)
    {
        x[i] = gsl_rng_uniform(rng);
        y[i] = gsl_rng_uniform(rng);
    }
    double d;
    for (int i = 0; i < vertices; ++i)
    {
        for (int j = 0; j < vertices; ++j)
        {
            const double a = x[i] - x[j];
            const double b = y[i] - y[j];
            d = hypot(a, b);
            if (d < r)
            {
                graph_add_edge(g, i, j, r - d);
            }
        }
    }
}

void graph_get_crgg(graph *g, int vertices, double r, double *x, double *y, gsl_rng *rng)
{
    graph_init(g, vertices);
    graph_get_rgg(g, vertices, r, x, y, rng);

    while (graph_strongly_connected(g) == FALSE)
    {
        graph_free(g);
        graph_get_rgg(g, vertices, r, x, y, rng);
    }
}

void graph_get_rec_rgg(graph *g, int vertices, double width, double r, double *x, double *y, gsl_rng *rng)
{
    graph_init(g, vertices);

    const double length = 1.0 / width; // A = l * w so l = 1 / w

    for (int i = 0; i < vertices; ++i)
    {
        x[i] = gsl_rng_uniform(rng) * length;
        y[i] = gsl_rng_uniform(rng) * width;
    }
    double d;
    for (int i = 0; i < vertices; ++i)
    {
        for (int j = 0; j < vertices; ++j)
        {
            const double a = x[i] - x[j];
            const double b = y[i] - y[j];
            d = hypot(a, b);
            if (d < r)
            {
                graph_add_edge(g, i, j, r - d);
            }
        }
    }
}

void graph_get_rec_crgg(graph *g, int vertices, double width, double r, double *x, double *y, gsl_rng *rng)
{
    graph_init(g, vertices);
    graph_get_rec_rgg(g, vertices, width, r, x, y, rng);

    while (graph_strongly_connected(g) == FALSE)
    {
        graph_free(g);
        graph_get_rec_rgg(g, vertices, width, r, x, y, rng);
    }
}

void graph_get_complete(graph *g, int vertices)
{
    graph_init(g, vertices);

    for (int i = 0; i < vertices; ++i)
    {
        for (int j = 0; j < vertices; ++j)
        {
            graph_add_edge(g, i, j, 1.0);
        }
    }
}

void graph_get_circle(graph *g, int vertices)
{
    graph_init(g, vertices);

    graph_add_edge(g, vertices - 1, vertices - 1, 1.0);
    graph_add_sym_edges(g, 0, vertices - 1, 1.0);

    for (int u = 0; u < vertices - 1; ++u)
    {
        graph_add_edge(g, u, u, 1.0);
        graph_add_sym_edges(g, u, u + 1, 1.0);
    }
}

void graph_get_star(graph *g, int vertices)
{
    graph_init(g, vertices);

    for (int u = 0; u < vertices; ++u)
    {
        graph_add_edge(g, u, u, 1.0);
        graph_add_sym_edges(g, u, 0, 1.0);
    }
}

///////////////////////////////////////////////////////////////
// 'Private' functions

void graph_test_cc(const graph *g, int *group, int u)
{
    const int num_e = g->num_e[u];
    int e = 0;
    for (; e < num_e; ++e)
    {
        const int head = g->adj_list[u][e];
        if (head != u && group[head] == FALSE)
        {
            group[head] = TRUE;
            graph_test_cc(g, group, head);
        }
    }
}

// Grow the edge list of some vertex v.
void graph_grow_lists(graph *g, int u)
{
    g->capacity[u] <<= 1;

    int *tmp_adj = (int*)malloc(g->capacity[u] * sizeof(int));
    double *tmp_w = (double*)malloc(g->capacity[u] * sizeof(double));

    const int num_e = g->num_e[u];
    int e = 0;
    for (; e < num_e; ++e)
    {
        tmp_adj[e] = g->adj_list[u][e];
        tmp_w[e] = g->w_list[u][e];
    }
    int *swap_adj = g->adj_list[u];
    double *swap_w = g->w_list[u];

    g->adj_list[u] = tmp_adj;
    g->w_list[u] = tmp_w;

    free(swap_adj);
    free(swap_w);
}