/* specfunc/bessel_zero.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/* Author:  G. Jungman */

#include <config.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_airy.h>
#include <gsl/gsl_sf_pow_int.h>
#include <gsl/gsl_sf_bessel.h>

#include "error.h"

#include "bessel_olver.h"

/* For Chebyshev expansions of the roots as functions of nu,
 * see [G. Nemeth, Mathematical Approximation of Special Functions].
 * This gives the fits for all nu and s <= 10.
 * I made the fits for other values of s myself [GJ].
 */

/* Chebyshev expansion: j_{nu,1} = c_k T_k*(nu/2), nu <= 2 */
static const double coef_jnu1_a[] = {
  3.801775243633476,
  1.360704737511120,
 -0.030707710261106,
  0.004526823746202,
 -0.000808682832134,
  0.000159218792489,
 -0.000033225189761,
  0.000007205599763,
 -0.000001606110397,
  0.000000365439424,
 -0.000000084498039,
  0.000000019793815,
 -0.000000004687054,
  0.000000001120052,
 -0.000000000269767,
  0.000000000065420,
 -0.000000000015961,
  0.000000000003914,
 -0.000000000000965,
  0.000000000000239,
 -0.000000000000059,
  0.000000000000015,
 -0.000000000000004,
  0.000000000000001
};

/* Chebyshev expansion: j_{nu,1} = nu c_k T_k*((2/nu)^(2/3)), nu >= 2 */
static const double coef_jnu1_b[] = {
  1.735063412537096,
  0.784478100951978,
  0.048881473180370,
 -0.000578279783021,
 -0.000038984957864,
  0.000005758297879,
 -0.000000327583229,
 -0.000000003853878,
  0.000000002284653,
 -0.000000000153079,
 -0.000000000000895,
  0.000000000000283,
  0.000000000000043,
  0.000000000000010,
 -0.000000000000003
};

/* Chebyshev expansion: j_{nu,2} = c_k T_k*(nu/2), nu <= 2 */
static const double coef_jnu2_a[] = {
  6.992370244046161,
  1.446379282056534,
 -0.023458616207293,
  0.002172149448700,
 -0.000246262775620,
  0.000030990180959,
 -0.000004154183047,
  0.000000580766328,
 -0.000000083648175,
  0.000000012317355,
 -0.000000001844887,
  0.000000000280076,
 -0.000000000042986,
  0.000000000006658,
 -0.000000000001039,
  0.000000000000163,
 -0.000000000000026,
  0.000000000000004,
 -0.000000000000001
};

/* Chebyshev expansion: j_{nu,2} = nu c_k T_k*((2/nu)^(2/3)), nu >= 2 */
static const double coef_jnu2_b[] = {
  2.465611864263400,
  1.607952988471069,
  0.138758034431497,
 -0.003687791182054,
 -0.000051276007868,
  0.000045113570749,
 -0.000007579172152,
  0.000000736469208,
 -0.000000011118527,
 -0.000000011919884,
  0.000000002696788,
 -0.000000000314488,
  0.000000000008124,
  0.000000000005211,
 -0.000000000001292,
  0.000000000000158,
 -0.000000000000004,
 -0.000000000000003,
  0.000000000000001
};

/* Chebyshev expansion: j_{nu,3} = c_k T_k*(nu/3), nu <= 3 */
static const double coef_jnu3_a[] = {
  10.869647065239236,
   2.177524286141710,
  -0.034822817125293,
   0.003167249102413,
  -0.000353960349344,
   0.000044039086085,
  -0.000005851380981,
   0.000000812575483,
  -0.000000116463617,
   0.000000017091246,
  -0.000000002554376,
   0.000000000387335,
  -0.000000000059428,
   0.000000000009207,
  -0.000000000001438,
   0.000000000000226,
  -0.000000000000036,
   0.000000000000006,
  -0.000000000000001
};

/* Chebyshev expansion: j_{nu,3} = nu c_k T_k*((3/nu)^(2/3)), nu >= 3 */
static const double coef_jnu3_b[] = {
  2.522816775173244,
  1.673199424973720,
  0.146431617506314,
 -0.004049001763912,
 -0.000039517767244,
  0.000048781729288,
 -0.000008729705695,
  0.000000928737310,
 -0.000000028388244,
 -0.000000012927432,
  0.000000003441008,
 -0.000000000471695,
  0.000000000025590,
  0.000000000005502,
 -0.000000000001881,
  0.000000000000295,
 -0.000000000000020,
 -0.000000000000003,
  0.000000000000001
};

/* Chebyshev expansion: j_{nu,4} = c_k T_k*(nu/4), nu <= 4 */
static const double coef_jnu4_a[] = {
  14.750310252773009,
   2.908010932941708,
  -0.046093293420315,
   0.004147172321412,
  -0.000459092310473,
   0.000056646951906,
  -0.000007472351546,
   0.000001031210065,
  -0.000000147008137,
   0.000000021475218,
  -0.000000003197208,
   0.000000000483249,
  -0.000000000073946,
   0.000000000011431,
  -0.000000000001782,
   0.000000000000280,
  -0.000000000000044,
   0.000000000000007,
  -0.000000000000001
};

/* Chebyshev expansion: j_{nu,4} = nu c_k T_k*((4/nu)^(2/3)), nu >= 4 */
static const double coef_jnu4_b[] = {
  2.551681323117914,
  1.706177978336572,
  0.150357658406131,
 -0.004234001378590,
 -0.000033854229898,
  0.000050763551485,
 -0.000009337464057,
  0.000001029717834,
 -0.000000037474196,
 -0.000000013450153,
  0.000000003836180,
 -0.000000000557404,
  0.000000000035748,
  0.000000000005487,
 -0.000000000002187,
  0.000000000000374,
 -0.000000000000031,
 -0.000000000000003,
  0.000000000000001
};

/* Chebyshev expansion: j_{nu,5} = c_k T_k*(nu/5), nu <= 5 */
static const double coef_jnu5_a[] = {
  18.632261081028211,
   3.638249012596966,
  -0.057329705998828,
   0.005121709126820,
  -0.000563325259487,
   0.000069100826174,
  -0.000009066603030,
   0.000001245181383,
  -0.000000176737282,
   0.000000025716695,
  -0.000000003815184,
   0.000000000574839,
  -0.000000000087715,
   0.000000000013526,
  -0.000000000002104,
   0.000000000000330,
  -0.000000000000052,
   0.000000000000008,
  -0.000000000000001
};

/* Chebyshev expansion: j_{nu,5} = nu c_k T_k*((5/nu)^(2/3)), nu >= 5 */
/* FIXME: There is something wrong with this fit, in about the
 * 9th or 10th decimal place.
 */
static const double coef_jnu5_b[] = {
  2.569079487591442,
  1.726073360882134,
  0.152740776809531,
 -0.004346449660148,
 -0.000030512461856,
  0.000052000821080,
 -0.000009713343981,
  0.000001091997863,
 -0.000000043061707,
 -0.000000013779413,
  0.000000004082870,
 -0.000000000611259,
  0.000000000042242,
  0.000000000005448,
 -0.000000000002377,
  0.000000000000424,
 -0.000000000000038,
 -0.000000000000002,
  0.000000000000002
};

/* Chebyshev expansion: j_{nu,6} = c_k T_k*(nu/6), nu <= 6 */
static const double coef_jnu6_a[] = {
  22.514836143374042,
   4.368367257557198,
  -0.068550155285562,
   0.006093776505822,
  -0.000667152784957,
   0.000081486022398,
  -0.000010649011647,
   0.000001457089679,
  -0.000000206105082,
   0.000000029894724,
  -0.000000004422012,
   0.000000000664471,
  -0.000000000101140,
   0.000000000015561,
  -0.000000000002416,
   0.000000000000378,
  -0.000000000000060,
   0.000000000000009,
  -0.000000000000002
};

/* Chebyshev expansion: j_{nu,6} = nu c_k T_k*((6/nu)^(2/3)), nu >= 6 */
static const double coef_jnu6_b[] = {
  2.580710285494837,
  1.739380728566154,
  0.154340696401691,
 -0.004422028860168,
 -0.000028305272624,
  0.000052845975269,
 -0.000009968794373,
  0.000001134252926,
 -0.000000046841241,
 -0.000000014007555,
  0.000000004251816,
 -0.000000000648213,
  0.000000000046728,
  0.000000000005414,
 -0.000000000002508,
  0.000000000000459,
 -0.000000000000043,
 -0.000000000000002,
  0.000000000000002
};

/* Chebyshev expansion: j_{nu,7} = c_k T_k*(nu/7), nu <= 7 */
static const double coef_jnu7_a[] = {
  26.397760539730869,
   5.098418721711790,
  -0.079761896398948,
   0.007064521280487,
  -0.000770766522482,
   0.000093835449636,
  -0.000012225308542,
   0.000001667939800,
  -0.000000235288157,
   0.000000034040347,
  -0.000000005023142,
   0.000000000753101,
  -0.000000000114389,
   0.000000000017564,
  -0.000000000002722,
   0.000000000000425,
  -0.000000000000067,
   0.000000000000011,
  -0.000000000000002
};

/* Chebyshev expansion: j_{nu,7} = nu c_k T_k*((7/nu)^(2/3)), nu >= 7 */
static const double coef_jnu7_b[] = {
  2.589033335856773,
  1.748907007612678,
  0.155488900387653,
 -0.004476317805688,
 -0.000026737952924,
  0.000053459680946,
 -0.000010153699240,
  0.000001164804272,
 -0.000000049566917,
 -0.000000014175403,
  0.000000004374840,
 -0.000000000675135,
  0.000000000050004,
  0.000000000005387,
 -0.000000000002603,
  0.000000000000485,
 -0.000000000000047,
 -0.000000000000002,
  0.000000000000002
};

/* Chebyshev expansion: j_{nu,8} = c_k T_k*(nu/8), nu <= 8 */
static const double coef_jnu8_a[] = {
  30.280900001606662,
   5.828429205461221,
  -0.090968381181069,
   0.008034479731033,
  -0.000874254899080,
   0.000106164151611,
  -0.000013798098749,
   0.000001878187386,
  -0.000000264366627,
   0.000000038167685,
  -0.000000005621060,
   0.000000000841165,
  -0.000000000127538,
   0.000000000019550,
  -0.000000000003025,
   0.000000000000472,
  -0.000000000000074,
   0.000000000000012,
  -0.000000000000002
};

/* Chebyshev expansion: j_{nu,8} = nu c_k T_k*((8/nu)^(2/3)), nu >= 8 */
static const double coef_jnu8_b[] = {
  2.595283877150078,
  1.756063044986928,
  0.156352972371030,
 -0.004517201896761,
 -0.000025567187878,
  0.000053925472558,
 -0.000010293734486,
  0.000001187923085,
 -0.000000051625122,
 -0.000000014304212,
  0.000000004468450,
 -0.000000000695620,
  0.000000000052500,
  0.000000000005367,
 -0.000000000002676,
  0.000000000000505,
 -0.000000000000050,
 -0.000000000000002,
  0.000000000000002
};

/* Chebyshev expansion: j_{nu,9} = c_k T_k*(nu/9), nu <= 9 */
static const double coef_jnu9_a[] = {
  34.164181213238386,
   6.558412747925228,
  -0.102171455365016,
   0.009003934361201,
  -0.000977663914535,
   0.000118479876579,
  -0.000015368714220,
   0.000002088064285,
  -0.000000293381154,
   0.000000042283900,
  -0.000000006217033,
   0.000000000928887,
  -0.000000000140627,
   0.000000000021526,
  -0.000000000003326,
   0.000000000000518,
  -0.000000000000081,
   0.000000000000013,
  -0.000000000000002
};

/* Chebyshev expansion: j_{nu,9} = nu c_k T_k*((9/nu)^(2/3)), nu >= 9 */
static const double coef_jnu9_b[] = {
  2.600150240905079,
  1.761635491694032,
  0.157026743724010,
 -0.004549100368716,
 -0.000024659248617,
  0.000054291035068,
 -0.000010403464334,
  0.000001206027524,
 -0.000000053234089,
 -0.000000014406241,
  0.000000004542078,
 -0.000000000711728,
  0.000000000054464,
  0.000000000005350,
 -0.000000000002733,
  0.000000000000521,
 -0.000000000000052,
 -0.000000000000002,
  0.000000000000002
};

/* Chebyshev expansion: j_{nu,10} = c_k T_k*(nu/10), nu <= 10 */
static const double coef_jnu10_a[] = {
  38.047560766184647,
   7.288377637926008,
  -0.113372193277897,
   0.009973047509098,
  -0.001081019701335,
   0.000130786983847,
  -0.000016937898538,
   0.000002297699179,
  -0.000000322354218,
   0.000000046392941,
  -0.000000006811759,
   0.000000001016395,
  -0.000000000153677,
   0.000000000023486,
  -0.000000000003616,
   0.000000000000561,
  -0.000000000000095,
   0.000000000000027,
  -0.000000000000013,
   0.000000000000005
};

/* Chebyshev expansion: j_{nu,10} = nu c_k T_k*((10/nu)^(2/3)), nu >= 10 */
static const double coef_jnu10_b[] = {
  2.604046346867949,
  1.766097596481182,
  0.157566834446511,
 -0.004574682244089,
 -0.000023934500688,
  0.000054585558231,
 -0.000010491765415,
  0.000001220589364,
 -0.000000054526331,
 -0.000000014489078,
  0.000000004601510,
 -0.000000000724727,
  0.000000000056049,
  0.000000000005337,
 -0.000000000002779,
  0.000000000000533,
 -0.000000000000054,
 -0.000000000000002,
  0.000000000000002
};

/* Chebyshev expansion: j_{nu,11} = c_k T_k*(nu/22), nu <= 22 */
static const double coef_jnu11_a[] = {
  49.5054081076848637,
  15.33692279367165101,
 -0.33677234163517130,
  0.04623235772920729,
 -0.00781084960665093,
  0.00147217395434708,
 -0.00029695043846867,
  0.00006273356860235,
 -0.00001370575125628,
  3.07171282012e-6,
 -7.0235041249e-7,
  1.6320559339e-7,
 -3.843117306e-8,
  9.15083800e-9,
 -2.19957642e-9,
  5.3301703e-10,
 -1.3007541e-10,
  3.193827e-11,
 -7.88605e-12,
  1.95918e-12,
 -4.9020e-13,
  1.2207e-13,
 -2.820e-14,
  5.25e-15,
 -1.88e-15,
  2.80e-15,
 -2.45e-15
};

/* Chebyshev expansion: j_{nu,12} = c_k T_k*(nu/24), nu <= 24 */
static const double coef_jnu12_a[] = {
  54.0787833216641519,
  16.7336367772863598,
 -0.36718411124537953,
  0.05035523375053820,
 -0.00849884978867533,
  0.00160027692813434,
 -0.00032248114889921,
  0.00006806354127199,
 -0.00001485665901339,
  3.32668783672e-6,
 -7.5998952729e-7,
  1.7644939709e-7,
 -4.151538210e-8,
  9.87722772e-9,
 -2.37230133e-9,
  5.7442875e-10,
 -1.4007767e-10,
  3.437166e-11,
 -8.48215e-12,
  2.10554e-12,
 -5.2623e-13,
  1.3189e-13,
 -3.175e-14,
  5.73e-15,
  5.6e-16,
 -8.7e-16,
 -6.5e-16
};

/* Chebyshev expansion: j_{nu,13} = c_k T_k*(nu/26), nu <= 26 */
static const double coef_jnu13_a[] = {
  58.6521941921708890,
  18.1303398137970284,
 -0.39759381380126650,
  0.05447765240465494,
 -0.00918674227679980,
  0.00172835361420579,
 -0.00034800528297612,
  0.00007339183835188,
 -0.00001600713368099,
  3.58154960392e-6,
 -8.1759873497e-7,
  1.8968523220e-7,
 -4.459745253e-8,
  1.060304419e-8,
 -2.54487624e-9,
  6.1580214e-10,
 -1.5006751e-10,
  3.679707e-11,
 -9.07159e-12,
  2.24713e-12,
 -5.5943e-13,
  1.4069e-13,
 -3.679e-14,
  1.119e-14,
 -4.99e-15,
  3.43e-15,
 -2.85e-15,
  2.3e-15,
 -1.7e-15,
  8.7e-16
};

/* Chebyshev expansion: j_{nu,14} = c_k T_k*(nu/28), nu <= 28 */
static const double coef_jnu14_a[] = {
  63.2256329577315566,
  19.5270342832914901,
 -0.42800190567884337,
  0.05859971627729398,
 -0.00987455163523582,
  0.00185641011402081,
 -0.00037352439419968,
  0.00007871886257265,
 -0.00001715728110045,
  3.83632624437e-6,
 -8.7518558668e-7,
  2.0291515353e-7,
 -4.767795233e-8,
  1.132844415e-8,
 -2.71734219e-9,
  6.5714886e-10,
 -1.6005342e-10,
  3.922557e-11,
 -9.66637e-12,
  2.39379e-12,
 -5.9541e-13,
  1.4868e-13,
 -3.726e-14,
  9.37e-15,
 -2.36e-15,
  6.0e-16
};

/* Chebyshev expansion: j_{nu,15} = c_k T_k*(nu/30), nu <= 30 */
static const double coef_jnu15_a[] = {
  67.7990939565631635,
  20.9237219226859859,
 -0.45840871823085836,
  0.06272149946755639,
 -0.01056229551143042,
  0.00198445078693100,
 -0.00039903958650729,
  0.00008404489865469,
 -0.00001830717574922,
  4.09103745566e-6,
 -9.3275533309e-7,
  2.1614056403e-7,
 -5.075725222e-8,
  1.205352081e-8,
 -2.88971837e-9,
  6.9846848e-10,
 -1.7002946e-10,
  4.164941e-11,
 -1.025859e-11,
  2.53921e-12,
 -6.3128e-13,
  1.5757e-13,
 -3.947e-14,
  9.92e-15,
 -2.50e-15,
  6.3e-16
};

/* Chebyshev expansion: j_{nu,16} = c_k T_k*(nu/32), nu <= 32 */
static const double coef_jnu16_a[] = {
  72.3725729616724770,
  22.32040402918608585,
 -0.48881449782358690,
  0.06684305681828766,
 -0.01124998690363398,
  0.00211247882775445,
 -0.00042455166484632,
  0.00008937015316346,
 -0.00001945687139551,
  4.34569739281e-6,
 -9.9031173548e-7,
  2.2936247195e-7,
 -5.383562595e-8,
  1.277835103e-8,
 -3.06202860e-9,
  7.3977037e-10,
 -1.8000071e-10,
  4.407196e-11,
 -1.085046e-11,
  2.68453e-12,
 -6.6712e-13,
  1.6644e-13,
 -4.168e-14,
  1.047e-14,
 -2.64e-15,
  6.7e-16
};

/* Chebyshev expansion: j_{nu,17} = c_k T_k*(nu/34), nu <= 34 */
static const double coef_jnu17_a[] = {
  76.9460667535209549,
  23.71708159112252670,
 -0.51921943142405352,
  0.07096442978067622,
 -0.01193763559341369,
  0.00224049662974902,
 -0.00045006122941781,
  0.00009469477941684,
 -0.00002060640777107,
  4.60031647195e-6,
 -1.04785755046e-6,
  2.4258161247e-7,
 -5.691327087e-8,
  1.350298805e-8,
 -3.23428733e-9,
  7.8105847e-10,
 -1.8996825e-10,
  4.649350e-11,
 -1.144205e-11,
  2.82979e-12,
 -7.0294e-13,
  1.7531e-13,
 -4.388e-14,
  1.102e-14,
 -2.78e-15,
  7.0e-16
};

/* Chebyshev expansion: j_{nu,18} = c_k T_k*(nu/36), nu <= 36 */
static const double coef_jnu18_a[] = {
  81.5195728368096659,
  25.11375537470259305,
 -0.54962366347317668,
  0.07508565026117689,
 -0.01262524908033818,
  0.00236850602019778,
 -0.00047556873651929,
  0.00010001889347161,
 -0.00002175581482429,
  4.85490251239e-6,
 -1.10539483940e-6,
  2.5579853343e-7,
 -5.999033352e-8,
  1.422747129e-8,
 -3.40650521e-9,
  8.2233565e-10,
 -1.9993286e-10,
  4.891426e-11,
 -1.203343e-11,
  2.97498e-12,
 -7.3875e-13,
  1.8418e-13,
 -4.608e-14,
  1.157e-14,
 -2.91e-15,
  7.4e-16
};

/* Chebyshev expansion: j_{nu,19} = c_k T_k*(nu/38), nu <= 38 */
static const double coef_jnu19_a[] = {
  86.0930892477047512,
  26.51042598308271729,
 -0.58002730731948358,
  0.07920674321589394,
 -0.01331283320930301,
  0.00249650841778073,
 -0.00050107453900793,
  0.00010534258471335,
 -0.00002290511552874,
  5.10946148897e-6,
 -1.16292517157e-6,
  2.6901365037e-7,
 -6.306692473e-8,
  1.495183048e-8,
 -3.57869025e-9,
  8.6360410e-10,
 -2.0989514e-10,
  5.133439e-11,
 -1.262465e-11,
  3.12013e-12,
 -7.7455e-13,
  1.9304e-13,
 -4.829e-14,
  1.212e-14,
 -3.05e-15,
  7.7e-16
};

/* Chebyshev expansion: j_{nu,20} = c_k T_k*(nu/40), nu <= 40 */
static const double coef_jnu20_a[] = {
  90.6666144195163770,
  27.9070938975436823,
 -0.61043045315390591,
  0.08332772844325554,
 -0.01400039260208282,
  0.00262450494035660,
 -0.00052657891389470,
  0.00011066592304919,
 -0.00002405432778364,
  5.36399803946e-6,
 -1.22044976064e-6,
  2.8222728362e-7,
 -6.614312964e-8,
  1.567608839e-8,
 -3.75084856e-9,
  9.0486546e-10,
 -2.1985553e-10,
  5.375401e-11,
 -1.321572e-11,
  3.26524e-12,
 -8.1033e-13,
  2.0190e-13,
 -5.049e-14,
  1.267e-14,
 -3.19e-15,
  8.0e-16,
 -2.0e-16
};

static const double * coef_jnu_a[] = {
  0,
  coef_jnu1_a,
  coef_jnu2_a,
  coef_jnu3_a,
  coef_jnu4_a,
  coef_jnu5_a,
  coef_jnu6_a,
  coef_jnu7_a,
  coef_jnu8_a,
  coef_jnu9_a,
  coef_jnu10_a,
  coef_jnu11_a,
  coef_jnu12_a,
  coef_jnu13_a,
  coef_jnu14_a,
  coef_jnu15_a,
  coef_jnu16_a,
  coef_jnu17_a,
  coef_jnu18_a,
  coef_jnu19_a,
  coef_jnu20_a
};

static const size_t size_jnu_a[] = {
  0,
  sizeof(coef_jnu1_a)/sizeof(double),
  sizeof(coef_jnu2_a)/sizeof(double),
  sizeof(coef_jnu3_a)/sizeof(double),
  sizeof(coef_jnu4_a)/sizeof(double),
  sizeof(coef_jnu5_a)/sizeof(double),
  sizeof(coef_jnu6_a)/sizeof(double),
  sizeof(coef_jnu7_a)/sizeof(double),
  sizeof(coef_jnu8_a)/sizeof(double),
  sizeof(coef_jnu9_a)/sizeof(double),
  sizeof(coef_jnu10_a)/sizeof(double),
  sizeof(coef_jnu11_a)/sizeof(double),
  sizeof(coef_jnu12_a)/sizeof(double),
  sizeof(coef_jnu13_a)/sizeof(double),
  sizeof(coef_jnu14_a)/sizeof(double),
  sizeof(coef_jnu15_a)/sizeof(double),
  sizeof(coef_jnu16_a)/sizeof(double),
  sizeof(coef_jnu17_a)/sizeof(double),
  sizeof(coef_jnu18_a)/sizeof(double),
  sizeof(coef_jnu19_a)/sizeof(double),
  sizeof(coef_jnu20_a)/sizeof(double)
};

static const double * coef_jnu_b[] = {
  0,
  coef_jnu1_b,
  coef_jnu2_b,
  coef_jnu3_b,
  coef_jnu4_b,
  coef_jnu5_b,
  coef_jnu6_b,
  coef_jnu7_b,
  coef_jnu8_b,
  coef_jnu9_b,
  coef_jnu10_b
};

static const size_t size_jnu_b[] = {
  0,
  sizeof(coef_jnu1_b)/sizeof(double),
  sizeof(coef_jnu2_b)/sizeof(double),
  sizeof(coef_jnu3_b)/sizeof(double),
  sizeof(coef_jnu4_b)/sizeof(double),
  sizeof(coef_jnu5_b)/sizeof(double),
  sizeof(coef_jnu6_b)/sizeof(double),
  sizeof(coef_jnu7_b)/sizeof(double),
  sizeof(coef_jnu8_b)/sizeof(double),
  sizeof(coef_jnu9_b)/sizeof(double),
  sizeof(coef_jnu10_b)/sizeof(double)
};

/* Evaluate Clenshaw recurrence for
 * a T* Chebyshev series.
 * sizeof(c) = N+1
 */
static double
clenshaw(const double * c, int N, double u)
{
  double B_np1 = 0.0;
  double B_n   = c[N];
  double B_nm1;
  int n;
  for(n=N; n>0; n--) {
    B_nm1 = 2.0*(2.0*u-1.0) * B_n - B_np1 + c[n-1];
    B_np1 = B_n;
    B_n   = B_nm1;
  }
  return B_n - (2.0*u-1.0)*B_np1;
}

/* correction terms to leading McMahon expansion
 * [Abramowitz+Stegun 9.5.12]
 * [Olver, Royal Society Math. Tables, v. 7]
 * We factor out a beta, so that this is a multiplicative
 * correction:
 *   j_{nu,s} = beta(s,nu) * mcmahon_correction(nu, beta(s,nu))
 *   macmahon_correction --> 1 as s --> Inf
 */
static double
mcmahon_correction(const double mu, const double beta)
{
  const double eb   = 8.0*beta;
  const double ebsq = eb*eb;

  if(mu < GSL_DBL_EPSILON) {
    /* Prevent division by zero below. */
    const double term1 =  1.0/ebsq;
    const double term2 = -4.0*31.0/(3*ebsq*ebsq);
    const double term3 =  32.0*3779.0/(15.0*ebsq*ebsq*ebsq);
    const double term4 = -64.0*6277237.0/(105.0*ebsq*ebsq*ebsq*ebsq);
    const double term5 =  512.0*2092163573.0/(315.0*ebsq*ebsq*ebsq*ebsq*ebsq);
    return 1.0 + 8.0*(term1 + term2 + term3 + term4 + term5);
  }
  else {
    /* Here we do things in terms of 1/mu, which
     * is purely to prevent overflow in the very
     * unlikely case that mu is really big.
     */
    const double mi   = 1.0/mu;
    const double r  = mu/ebsq;
    const double n2 = 4.0/3.0    * (7.0 - 31.0*mi);
    const double n3 = 32.0/15.0  * (83.0 + (-982.0 + 3779.0*mi)*mi);
    const double n4 = 64.0/105.0 * (6949.0 + (-153855.0 + (1585743.0 - 6277237.0*mi)*mi)*mi);
    const double n5 = 512.0/315.0 * (70197.0 + (-2479316.0 + (48010494.0 + (-512062548.0 + 2092163573.0*mi)*mi)*mi)*mi);
    const double n6 = 2048.0/3465.0 * (5592657.0 + (-287149133.0 + (8903961290.0 + (-179289628602.0 + (1982611456181.0 - 8249725736393.0*mi)*mi)*mi)*mi)*mi);
    const double term1 = (1.0 - mi) * r;
    const double term2 = term1 * n2 * r;
    const double term3 = term1 * n3 * r*r;
    const double term4 = term1 * n4 * r*r*r;
    const double term5 = term1 * n5 * r*r*r*r;
    const double term6 = term1 * n6 * r*r*r*r*r;
    return 1.0 - 8.0*(term1 + term2 + term3 + term4 + term5 + term6);
  }
}

/* Assumes z >= 1.0 */
static double
olver_b0(double z, double minus_zeta)
{
  if(z < 1.02) {
    const double a = 1.0-z;
    const double c0 =  0.0179988721413553309252458658183;
    const double c1 =  0.0111992982212877614645974276203;
    const double c2 =  0.0059404069786014304317781160605;
    const double c3 =  0.0028676724516390040844556450173;
    const double c4 =  0.0012339189052567271708525111185;
    const double c5 =  0.0004169250674535178764734660248;
    const double c6 =  0.0000330173385085949806952777365;
    const double c7 = -0.0001318076238578203009990106425;
    const double c8 = -0.0001906870370050847239813945647;
    return c0 + a*(c1 + a*(c2 + a*(c3 + a*(c4 + a*(c5 + a*(c6 + a*(c7 + a*c8)))))));
  }
  else {
    const double abs_zeta = minus_zeta;
    const double t = 1.0/(z*sqrt(1.0 - 1.0/(z*z)));
    return -5.0/(48.0*abs_zeta*abs_zeta) + t*(3.0 + 5.0*t*t)/(24.0*sqrt(abs_zeta));
  }
}

inline
static double
olver_f1(double z, double minus_zeta)
{
  const double b0 = olver_b0(z, minus_zeta);
  const double h2 = sqrt(4.0*minus_zeta/(z*z-1.0)); /* FIXME */
  return 0.5 * z * h2 * b0;
}

int
gsl_sf_bessel_zero_J0_e(unsigned int s, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(s == 0){
    result->val = 0.0;
    result->err = 0.0;
    GSL_ERROR ("error", GSL_EINVAL);
  }
  else {
    /* See [F. Lether, J. Comp. Appl .Math. 67, 167 (1996)]. */

    static const double P[] = { 1567450796.0/12539606369.0,
                                8903660.0/2365861.0,
                                10747040.0/536751.0,
                                17590991.0/1696654.0
                              };
    static const double Q[] = { 1.0,
                                29354255.0/954518.0,
                                76900001.0/431847.0,
                                67237052.0/442411.0
                              };

    const double beta = (s - 0.25) * M_PI;
    const double bi2  = 1.0/(beta*beta);
    const double R33num = P[0] + bi2 * (P[1] + bi2 * (P[2] + P[3] * bi2));
    const double R33den = Q[0] + bi2 * (Q[1] + bi2 * (Q[2] + Q[3] * bi2));
    const double R33 = R33num/R33den;
    result->val = beta + R33/beta;
    result->err = fabs(3.0e-15 * result->val);
    return GSL_SUCCESS;
  }
}

int
gsl_sf_bessel_zero_J1_e(unsigned int s, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(s == 0) {
    result->val = 0.0;
    result->err = 0.0;
    return GSL_SUCCESS;
  }
  else {
    /* See [M. Branders et al., J. Comp. Phys. 42, 403 (1981)]. */

    static const double a[] = { -0.362804405737084,
                                 0.120341279038597,
                                 0.439454547101171e-01,
                                 0.159340088474713e-02
                              };
    static const double b[] = {  1.0,
                                -0.325641790801361,
                                -0.117453445968927,
                                -0.424906902601794e-02
                              };

    const double beta = (s + 0.25) * M_PI;
    const double bi2  = 1.0/(beta*beta);
    const double Rnum = a[3] + bi2 * (a[2] + bi2 * (a[1] + bi2 * a[0]));
    const double Rden = b[3] + bi2 * (b[2] + bi2 * (b[1] + bi2 * b[0]));
    const double R = Rnum/Rden;
    result->val = beta * (1.0 + R*bi2);
    result->err = fabs(2.0e-14 * result->val);
    return GSL_SUCCESS;
  }
}

int
gsl_sf_bessel_zero_Jnu_e(double nu, unsigned int s, gsl_sf_result * result)
{
  /* CHECK_POINTER(result) */

  if(nu <= -1.0) {
    DOMAIN_ERROR(result);
  }
  else if(s == 0) {
    result->val = 0.0;
    result->err = 0.0;
    if (nu == 0.0) {
      GSL_ERROR ("no zero-th root for nu = 0.0", GSL_EINVAL);
    }
    return GSL_SUCCESS;
  }
  else if(nu < 0.0) {
    /* This can be done, I'm just lazy now. */
    result->val = 0.0;
    result->err = 0.0;
    GSL_ERROR("unimplemented", GSL_EUNIMPL);
  }
  else if(s == 1) {
    /* Chebyshev fits for the first positive zero.
     * For some reason Nemeth made this different from the others.
     */
    if(nu < 2.0) {
      const double * c = coef_jnu_a[s];
      const size_t   L = size_jnu_a[s];
      const double arg = nu/2.0;
      const double chb = clenshaw(c, L-1, arg);
      result->val = chb;
      result->err = 2.0e-15 * result->val;
    }
    else {
      const double * c = coef_jnu_b[s];
      const size_t   L = size_jnu_b[s];
      const double arg = pow(2.0/nu, 2.0/3.0);
      const double chb = clenshaw(c, L-1, arg);
      result->val = nu * chb;
      result->err = 2.0e-15 * result->val;
    }
    return GSL_SUCCESS;
  }
  else if(s <= 10) {
    /* Chebyshev fits for the first 10 positive zeros. */
    if(nu < s) {
      const double * c = coef_jnu_a[s];
      const size_t   L = size_jnu_a[s];
      const double arg = nu/s;
      const double chb = clenshaw(c, L-1, arg);
      result->val = chb;
      result->err = 2.0e-15 * result->val;
    }
    else {
      const double * c = coef_jnu_b[s];
      const size_t   L = size_jnu_b[s];
      const double arg = pow(s/nu, 2.0/3.0);
      const double chb = clenshaw(c, L-1, arg);
      result->val = nu * chb;
      result->err = 2.0e-15 * result->val;

      /* FIXME: truth in advertising for the screwed up
       * s = 5 fit. Need to fix that.
       */
      if(s == 5) {
        result->err *= 5.0e+06;
      }
    }
    return GSL_SUCCESS;
  }
  else if(s > 0.5*nu && s <= 20) {
    /* Chebyshev fits for 10 < s <= 20. */
    const double * c = coef_jnu_a[s];
    const size_t   L = size_jnu_a[s];
    const double arg = nu/(2.0*s);
    const double chb = clenshaw(c, L-1, arg);
    result->val = chb;
    result->err = 4.0e-15 * chb;
    return GSL_SUCCESS;
  }
  else if(s > 2.0 * nu) {
    /* McMahon expansion if s is large compared to nu. */
    const double beta = (s + 0.5*nu - 0.25) * M_PI;
    const double mc   = mcmahon_correction(4.0*nu*nu, beta);
    gsl_sf_result rat12;
    gsl_sf_pow_int_e(nu/beta, 14, &rat12);
    result->val  = beta * mc;
    result->err  = 4.0 * fabs(beta) * rat12.val;
    result->err += 4.0 * fabs(GSL_DBL_EPSILON * result->val);
    return GSL_SUCCESS;
  }
  else {
    /* Olver uniform asymptotic. */
    gsl_sf_result as;
    const int stat_as = gsl_sf_airy_zero_Ai_e(s, &as);
    const double minus_zeta = -pow(nu,-2.0/3.0) * as.val;
    const double z  = gsl_sf_bessel_Olver_zofmzeta(minus_zeta);
    const double f1 = olver_f1(z, minus_zeta);
    result->val  = nu * (z + f1/(nu*nu));
    result->err  = 0.001/(nu*nu*nu);
    result->err += 2.0 * GSL_DBL_EPSILON * fabs(result->val);
    return stat_as;
  }
}

/*-*-*-*-*-*-*-*-*-* Functions w/ Natural Prototypes *-*-*-*-*-*-*-*-*-*-*/

#include "eval.h"

double gsl_sf_bessel_zero_J0(unsigned int s)
{
  EVAL_RESULT(gsl_sf_bessel_zero_J0_e(s, &result));
}

double gsl_sf_bessel_zero_J1(unsigned int s)
{
  EVAL_RESULT(gsl_sf_bessel_zero_J1_e(s, &result));
}

double gsl_sf_bessel_zero_Jnu(double nu, unsigned int s)
{
  EVAL_RESULT(gsl_sf_bessel_zero_Jnu_e(nu, s, &result));
}


#include <stdlib.h>
#include <cblas.h>
#include <lapacke.h>

#include "../../runtime/pcp.h"
#include "tasks.h"
#include "utils.h"

#define BLOCK_SIZE 32
#define MAX_MATRIX_SIZE 1024
#define MAX_NUM_ITER (MAX_MATRIX_SIZE / BLOCK_SIZE)
#define MAX_NUM_THREADS 16

// The matrix partitioning for TRSM and SYRK in each iteration.
//
// A partitioning x of a range 0, ..., n - 1 over p threads is
// represented as an array x[] of length p + 1 such that x[k] is the
// start of the block assigned to thread k and x[k + 1] - x[k] is the
// size of the block assigned to thread k, for k = 0, ..., p - 1.
struct partition
{
    // The matrix size.
    int matrix_size;

    // The block size.
    int block_size;

    // The number of threads.
    int num_threads;

    // The number of iterations = ceil(matrix_size, block_size).
    int num_iter;

    // The partitionings for TRSM.
    int trsm[MAX_NUM_ITER][MAX_NUM_THREADS + 1];

    // The partitionings for SYRK.
    int syrk[MAX_NUM_ITER][MAX_NUM_THREADS + 1];
};

// Barrier used to synchronize the workers between iterations.
static pcp_barrier_t *barrier = NULL;

static struct partition *partition_db[MAX_MATRIX_SIZE + 1][MAX_NUM_THREADS + 1] = {NULL};
static struct partition *partition = NULL;

static double *A;
static int ldA;
static int n;
#define A(i,j) A[(i) + (j) * ldA]

// Initializes the partitioning for TRSM for a given iteration.
//
// Uses uniform row blocks.
static void part_init_trsm(int iter)
{
    const int last = partition->matrix_size;
    const int first = min(last, (iter + 1) * partition->block_size);
    const int size = last - first;
    const int p = partition->num_threads;
    const int chunk = iceil(size, p);
    partition->trsm[iter][0] = first;
    partition->trsm[iter][p] = last;
    for (int th = 1; th < p; ++th) {
        partition->trsm[iter][th] = min(last, partition->trsm[iter][th - 1] + chunk);
    }
}

// Initializes the partitioning for SYRK for a given iteration.
static void part_init_syrk(int iter)
{
    // Balance the load by flops.
    const int last = partition->matrix_size;
    const int first = min(last, (iter + 1) * partition->block_size);
    const int size = last - first;
    const int p = partition->num_threads;
    const int total_work = size * (size + 1) / 2;
    const int ideal_part_work = total_work / p;
    partition->syrk[iter][0] = first;
    partition->syrk[iter][p] = last;
    for (int k = 1; k < p; ++k) {
        partition->syrk[iter][k] = partition->syrk[iter][k - 1];
        int work = 0;
        while (work < ideal_part_work && partition->syrk[iter][k] < last) {
            partition->syrk[iter][k] += 1;
            work += partition->syrk[iter][k] - first;
        }
        if (k == 1) {
            partition->syrk[iter][k] = max(partition->syrk[iter][k], partition->syrk[iter][k - 1] + partition->block_size);
            partition->syrk[iter][k] = min(partition->syrk[iter][k], last);
        }
    }
}

// Returns the start of a TRSM block.
static int part_block_start_trsm(int iter, int me)
{
    return partition->trsm[iter][me];
}

// Returns the size of a TRSM block.
static int part_block_size_trsm(int iter, int me)
{
    return part_block_start_trsm(iter, me + 1) - part_block_start_trsm(iter, me);
}

// Returns the start of a SYRK block.
static int part_block_start_syrk(int iter, int me)
{
    return partition->syrk[iter][me];
}

// Returns the size of a SYRK block.
static int part_block_size_syrk(int iter, int me)
{
    return part_block_start_syrk(iter, me + 1) - part_block_start_syrk(iter, me);
}

// Initializes the partitioning for given parameters.
//
// Follows this protocol:
// 1) Re-use existing if it matches.
// 2) Load from file if it matches.
// 3) Create from scratch.
static void part_init(int matrix_size, int block_size, int num_threads)
{
    // Check if the partitioning is available in the databse.
    if (partition_db[matrix_size][num_threads] == NULL) {
        // No, create from scratch and save in the database.
        partition = malloc(sizeof(*partition));
        partition->matrix_size = matrix_size;
        partition->block_size = block_size;
        partition->num_threads = num_threads;
        partition->num_iter = iceil(matrix_size, block_size);
        for (int iter = 0; iter < partition->num_iter; ++iter) {
            part_init_trsm(iter);
            part_init_syrk(iter);
        }
        partition_db[matrix_size][num_threads] = partition;
    } else {
        // Yes, reuse from database.
        partition = partition_db[matrix_size][num_threads];
    }
}

void chol_task_par_reconfigure(int nth)
{
    // Choose current size of the worker pool for barrier.
    if (barrier != NULL) {
        pcp_barrier_destroy(barrier);
    }
    barrier = pcp_barrier_create(nth);
}

void chol_task_par_finalize(void)
{
    if (barrier != NULL) {
        pcp_barrier_destroy(barrier);
        barrier = NULL;
    }
}

static void krnl_chol(int iter)
{
    int jp      = iter * partition->block_size;
    int n       = min(partition->matrix_size - jp, partition->block_size);
    double *A11 = &A(jp,jp);

    LAPACKE_dpotrf(LAPACK_COL_MAJOR, 'L', n, A11, ldA);
}

static void krnl_syrk(int iter, int me)
{
    //   j1        j2
    // i g g g g g s
    //   g g g g g s s
    //   g g g g g s s s
    //   g g g g g s s s s
    //
    // g = gemm
    // s = syrk

    int jp = iter * partition->block_size;
    int i  = part_block_start_syrk(iter, me);
    int m  = part_block_size_syrk(iter, me);
    int j1 = jp + partition->block_size;
    int j2 = i;
    int n1 = j2 - j1;
    int k  = partition->block_size;

    double *A21;
    double *A21a, *A21b;
    double *A22;

    /* SYRK */
    if (m > 0) {
        A22 = &A(i,j2);
        A21 = &A(i,jp);
        cblas_dsyrk(CblasColMajor, CblasLower, CblasNoTrans,
                    m, k,
                    -1.0, A21, ldA,
                     1.0, A22, ldA);
    }

    /* GEMM */
    if (m > 0 && n1 > 0) {
        A22  = &A(i,j1);
        A21a = &A(i,jp);
        A21b = &A(j1,jp);
        cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans,
                    m, n1, k,
                    -1.0, A21a, ldA,
                          A21b, ldA,
                     1.0, A22,  ldA);
    }
}

static void krnl_trsm(int iter, int me)
{
    int jp      = iter * partition->block_size;
    int i       = part_block_start_trsm(iter, me);
    int m       = part_block_size_trsm(iter, me);
    int n       = min(partition->matrix_size - jp, partition->block_size);
    double *A11 = &A(jp,jp);
    double *A21 = &A(i,jp);

    if (m > 0) {
        cblas_dtrsm(CblasColMajor, CblasRight, CblasLower, CblasTrans, CblasNonUnit,
                    m, n,
                    1.0, A11, ldA,
                         A21, ldA);
    }
}

void chol_task_par(void *ptr, int nth, int me)
{
    struct chol_task_arg *arg = (struct chol_task_arg*) ptr;

    n   = arg->n;
    A   = arg->A;
    ldA = arg->ldA;

    if (me == 0) {
        part_init(n, BLOCK_SIZE, nth);
    }

    if (me == 0) {
        // chol(0)
        krnl_chol(0);
    }

    // Synchronize.
    pcp_barrier_wait(barrier);

    // Loop over iterations.
    for (int iter = 0; iter < partition->num_iter; ++iter) {
        const int final_iteration = (iter == partition->num_iter - 1);

        // Synchronize
        pcp_barrier_wait(barrier);

        //
        // Phase I: trsm(iter) in //
        //

        krnl_trsm(iter, me);

        // Synchronize
        pcp_barrier_wait(barrier);

        //
        // Phase II: syrk(iter) in // plus chol(iter + 1)
        //

        krnl_syrk(iter, me);
        if (me == 0 && !final_iteration) {
            krnl_chol(iter + 1);
        }
    }

    // Synchronize
    pcp_barrier_wait(barrier);
}


#if !defined (STATISTICS_H)
#define STATISTICS_H

#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <vector>
#include <algorithm>
#include "Rand.h"
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_sf_bessel.h>

using namespace std;

const int         XMAX = 600;
const long double EXPXMAX = exp((double)XMAX);
const long double EXP     = exp(1.0);

double Beta(double t, double x, double Itot);
double Gaussian(double x, double mean, double var);
long double Poisson(int lambda, int k);

void Statistics(const vector<double>& data, double& ave, double& squareave, double& var);

void Statistics(const vector<double>& data, double& max, double& min, double& ave, double& var, double& error);
void Statistics(const vector<int>& data, double& max, double& min, double& ave, double& var, double& error);
void Statistics(const vector<int>& data, double& xmax, double& xmin, double& xave, double& xvar, double& xerror, double& x2ave);

//void Statistics(const vector<int>& data, double& xmax, double& xmin, double& xave, double& xvar, double& xerror, double& x2ave, double& x1inverseave);

void Statistics(const vector<int>& data, double& Xmin, double& Xmax,
		double& X1inverseave, //  <1/(X+1)>
		double& logXave,      //  < logX >
		double& Xsrave,       //  < X^0.5 >
		double& Xave,         //  < X >
		double& X2ave );      //  < x^2 >

void moment(const vector<int>& data, double &ave, double &adev, double &sdev, double &var, double &skew, double &curt);

void StatisticalDispersion(vector<int>& X, double& H7, double& H8, double& H9, double& H10);

double GetXsecondmin(const vector<double>& data, double Xmin);

void Statistics(const vector<double>& data,
		double& min, double& Q1, double& Q2, double& Q3, double& max,
		double& ave, double& var, double& error,
		ofstream& fout1, ofstream& fout2);

void Binning(const vector<double>& data, int B, vector<double>& Binsize, vector<double>& Q);
int GetBinIndex(const double& x, const vector<double>& Q);

#endif


#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>
#include "Watershed.h"
#include "math_functions.h"
#include "Globals.h"
#include "constitutive_equations.h"
#include "dgshed_numerical_flux.h"
#include "manufactured_solution.h"

/*****************************************************************************************//**
 * @file computeL.c
 *
 * This file contains code to evaluate the right hand side of the following discrete ODE obtained
 * from the DG discretization of the 2-D shallow water equations:
 *
 * \f$\frac{\partial \hat{\mathbf{w}}}{\partial t} = M^{-1}L(\hat{\mathbf{w}},t)\f$
 *
 * *******************************************************************************************/

/***************************************************************************/
/************ Compute the spatial discretization of the equation ***********/
/***************************************************************************/
extern void GetLGLWeights(int N, double *w);
extern double receive_floodwater(struct TwoDRegion *floodplain, int edg);

void computeChanL(struct channel* Chan, double time, double *dt, int channelNumber, int stage, double* RHSA, double* RHSQ)
{
	int fluxType = 1;
	int NumEdges = Chan->NumEdges;
	int NumEl = Chan->NumEl;
	int NumNodes = Chan->NumNodes;
	int Np = Chan->Np;
	int P = Chan->P;

#ifdef WDON
	/**** Compute the mass over an element to use later for ensuring ********/
	/**** positive mass with the flux in a wetting and drying treatment *************/
	double* mass = xcalloc(NumEl, sizeof(double));
	double LGLWeight[Np];
	GetLGLWeights(P, LGLWeight);

	for (int i = 0; i < NumEl; ++i)
	{
		double avgArea = 0;
		int begNode = i*Np + 1;
		for (int j =0; j < Np; j++)
		{
			avgArea += LGLWeight[j]*Chan->A[begNode+j];
		}
		mass[i] = avgArea;
	}
#endif

	/************** Compute the numerical flux at the faces *****************/
	double* Fhat1L = xcalloc(NumEdges, sizeof(double));
	double* Fhat2L = xcalloc(NumEdges, sizeof(double));
	double* Fhat1R = xcalloc(NumEdges, sizeof(double));
	double* Fhat2R = xcalloc(NumEdges, sizeof(double));

	for (int i=0; i < NumEdges; ++i)
	{
		double A_L, Q_L, A_R, Q_R, m1val, m2val;

		int leftNode = i*Np;
		int rightNode = leftNode+1;

		m1val = Chan->m1[i];
		m2val = Chan->m2[i];
		double bval = Chan->b[i];

		double tmpF[2];

#ifdef WDON
		// Check to see if the elements separated by this boundary are both dry
		if (i > 0 && i < NumEdges-1)
		{
			if (Chan->WD[i-1] == 0 && Chan->WD[i] == 0)
			{
				// Reflection flux for the left element
				A_L = Chan->A[leftNode];
				A_R = A_L;
				Q_L = Chan->Q[leftNode];
				Q_R = -Q_L;

				if (fluxType == 1)
					RoeFluxChan(tmpF, A_L, A_R, Q_L, Q_R,bval, m1val, m2val,0);
				else if (fluxType == 2)
					LFChan(tmpF, A_L, A_R, Q_L, Q_R,bval, m1val, m2val, 0);

				else
				{
					printf("Unknown flux type. Exiting now \n");
					exit(1);
				}

				Fhat1L[i] = tmpF[0];
				Fhat2L[i] = tmpF[1];

				// Reflection flux for the right element
				A_R = Chan->A[rightNode];
				A_L = A_R;
				Q_R = Chan->Q[rightNode];
				Q_L = -Q_R;

				if (fluxType == 1)
					RoeFluxChan(tmpF, A_L, A_R, Q_L, Q_R, bval, m1val, m2val, 0);
				else if (fluxType == 2)
					LFChan(tmpF, A_L, A_R, Q_L, Q_R, bval, m1val, m2val, 0);
				else
				{
					printf("Unknown flux type. Exiting now \n");
					exit(1);
				}
				Fhat1R[i] = tmpF[0];
				Fhat2R[i] = tmpF[1];

				if (isnan(tmpF[0]) || isnan(tmpF[1]))
				{
					printf("both elements dry flux not a number, edge %d \n",i);
					exit(EXIT_FAILURE);
				}

			}

		}

		// if the elements are not both dry
		if ((i == 0) || (i == NumEl) || Chan->WD[i-1] == 1 || Chan->WD[i] == 1)
		{
			A_L = Chan->A[leftNode];
			A_R = Chan->A[rightNode];

			Q_L = Chan->Q[leftNode];
			Q_R = Chan->Q[rightNode];

			if (fluxType == 1)
				RoeFluxChan(tmpF, A_L, A_R, Q_L,Q_R,bval, m1val, m2val, g);
			else if (fluxType == 2)
				LFChan(tmpF, A_L, A_R, Q_L,Q_R,bval, m1val, m2val, g);

			else
			{
				printf("Unknown flux type. Exiting now \n");
				exit(1);
			}

			if (isnan(tmpF[0]) || isnan(tmpF[1]))
			{
				printf("A_L = %lf A_R = %lf Q_L = %lf Q_R = %lf\n", A_L, A_R, Q_L, Q_R);
				printf(" both elements wet flux not a number, edge %d, channel %d , time = %lf\n",i, channelNumber, time);
				exit(EXIT_FAILURE);
			}

			if (i==0 || Chan->WD[i-1] == 1)
			{
				Fhat1L[i] = tmpF[0];
				Fhat2L[i] = tmpF[1];

			}
			else
			{
				double newtmpF[2];
				if (fluxType == 1)
					RoeFluxChan(newtmpF, A_L, A_R, Q_L, Q_R, bval, m1val, m2val, 0);
				else if (fluxType == 2)
					LFChan(newtmpF, A_L, A_R, Q_L, Q_R, bval, m1val, m2val, 0);
				else
				{
					printf("Unknown flux type. Exiting now \n");
					exit(1);
				}
				Fhat1L[i] = newtmpF[0];
				Fhat2L[i] = newtmpF[1];
				if (isnan(newtmpF[0]) || isnan(newtmpF[1]))
				{
					printf("left element dry flux not a number, edge %d \n",i);
					exit(EXIT_FAILURE);
				}

			}
			if (i == NumEl || Chan->WD[i]==1)
			{
				Fhat1R[i] = tmpF[0];
				Fhat2R[i] = tmpF[1];
			}
			else
			{
				if (fluxType == 1)
					RoeFluxChan(tmpF, A_L,A_R,Q_L,Q_R,bval, m1val, m2val, 0);
				else if (fluxType == 2)
					LFChan(tmpF, A_L,A_R,Q_L,Q_R,bval, m1val, m2val, 0);
				else
				{
					printf("Unknown flux type. Exiting now \n");
					exit(1);
				}
				Fhat1R[i] = tmpF[0];
				Fhat2R[i] = tmpF[1];
				if (isnan(tmpF[0]) || isnan(tmpF[1]))
				{
					printf("right element dry flux not a number, edge %d \n",i);
					printf("A_L = %lf A_R = %lf Q_L = %lf Q_R = %lf bval = %lf m1val = %lf m2val = %lf\n", A_L, A_R, Q_L, Q_R, bval, m1val, m2val);
					printf("F = %lf\n", tmpF[0]);
					exit(EXIT_FAILURE);
				}

			}
		}

#else
		A_L = Chan->A[leftNode];
		A_R = Chan->A[rightNode];

		Q_L = Chan->Q[leftNode];
		Q_R = Chan->Q[rightNode];

		if (fluxType == 1)
			RoeFluxChan(tmpF, A_L, A_R, Q_L,Q_R,bval,m1val,m2val,g);
		else if (fluxType == 2)
			LFChan(tmpF, A_L, A_R, Q_L,Q_R,bval,m1val,m2val,g);
		else
		{
			printf("Unknown flux type. Exiting now \n");
			exit(1);
		}

		if (isnan(tmpF[0]) || isnan(tmpF[1]))
		{
			printf("flux not a number, edge %d \n",i);

			printf("A_L = %3.16f A_R = %3.16f Q_L = %3.16f Q_R = %31.6f, b = %lf, m1 = %lf, m2 = %lf \n", A_L, A_R, Q_L, Q_R, Chan->b[i], m1val, m2val);

			printf("Fhat = %lf Ghat = %lf\n", tmpF[0], tmpF[1]);
			exit(EXIT_FAILURE);
		}

		Fhat1L[i] = tmpF[0];
		Fhat2L[i] = tmpF[1];

		Fhat1R[i] = tmpF[0];
		Fhat2R[i] = tmpF[1];

#endif

		/**************************************************************************************************/
		double u_L = Q_L/A_L;
		double u_R = Q_R/A_R;
		//approximate c_L (exact for rectangular channels, but not for trapezoidal
		double c_L = sqrt(g*A_L/Chan->b[i]);
		double c_R = sqrt(g*A_R/Chan->b[i]);

		if (i==0)
			Chan->max_lambda = fmax((fabs(u_L)+c_L), (fabs(u_R)+c_R));
		// Compute maximum eigenvalue for the next time step
		double current_max =fmax((fabs(u_L) + c_L), (fabs(u_R) + c_R));
		Chan->max_lambda = max(Chan->max_lambda,current_max);
	}

#ifdef WDON
	for (int i =1; i < NumEdges-1; ++i)
	{
		int leftNode = i*Np;
		int rightNode = leftNode+1;
		double maxBetaOverAlpha = 2;
		// Check to see if this flux might possibly result in negative mass
		// If the mass will be negative on the left side
		//int cont = 1;
		//while(cont)
		//{
			if (Fhat1L[i]*maxBetaOverAlpha*(*dt) > mass[i-1] || -Fhat1R[i]*maxBetaOverAlpha*(*dt) > mass[i] )
			{
				Fhat1L[i] = 0.0;
				Fhat2L[i] = 0.0;
				Fhat1R[i] = 0.0;
				Fhat2R[i] = 0.0;

				//double A_L = Chan->A[leftNode];
				//double A_R = A_L;
				//double Q_L = Chan->Q[leftNode];
				//double Q_R = -Q_L;
				//double bval = Chan->NodalB[leftNode];
				//double m1val = Chan->Nodalm1[leftNode];
				//double m2val = Chan->Nodalm2[leftNode];

				//double tmpF[2];
				//double localG = g;

				//if (Chan->WD[i] == 0)
				//	localG = 0;

				//if (fluxType == 1)
				//	RoeFluxChan(tmpF, A_L, A_R, Q_L, Q_R, bval,m1val,m2val,localG);
				//else if (fluxType == 2)
				//	LFChan(tmpF, A_L, A_R, Q_L, Q_R, bval,m1val,m2val,localG);
				//else
				//{
				//	printf("Unknown flux type. Exiting now \n");
				//	exit(1);
				//}
				//Fhat1L[i] = tmpF[0];
				//Fhat2L[i] = tmpF[1];
				//printf("element %d of Channel %d will be dry. Reducing dt now.\n", i, channelNumber);
				//(*dt) = 0.5*(*dt);
			}
			//else
			//	cont = 0;
		//}

		//cont = 1;
		//while(cont)
		//{
		//	// If the mass will be negative on the right side
		//	if (-Fhat1R[i]*maxBetaOverAlpha*(*dt) > mass[i])
		//	{
		//		double A_R = Chan->A[rightNode];
		//		double A_L = A_R;
		//		double Q_R = Chan->Q[rightNode];
		//		double Q_L = -Q_R;
		//		double bval = Chan->NodalB[rightNode];
		//		double m1val = Chan->Nodalm1[rightNode];
		//		double m2val = Chan->Nodalm2[rightNode];

		//		double tmpF[2];
		//		double localG = g;
		//		if (Chan->WD[i] == 0)
		//			localG = 0;

		//		if (fluxType == 1)
		//			RoeFluxChan(tmpF, A_L, A_R, Q_L, Q_R, bval, m1val, m2val,localG);
		//		else if (fluxType == 2)
		//			LFChan(tmpF, A_L, A_R, Q_L, Q_R, bval,m1val,m2val,localG);
		//		else
		//		{
		//			printf("Unknown flux type. Exiting now \n");
		//			exit(1);
		//		}

		//		Fhat1R[i] = tmpF[0];
		//		Fhat2R[i] = tmpF[1];
		//		printf("element %d of channel %d will be dry. Decreasing dt now.\n",i, channelNumber);

		//		*dt = 0.5*(*dt);

		//	}
		//	else
		//		cont = 0;
		//}
	}
	free(mass);
#endif

	for (int k=0; k < NumEl; ++k)
	{

		double h = Chan->dh[k];
		gsl_vector *F1 = gsl_vector_alloc(Np);
		gsl_vector *F2 = gsl_vector_alloc(Np);
		gsl_vector *ST21 = gsl_vector_alloc(Np);
		gsl_vector *ST22 = gsl_vector_alloc(Np);
		gsl_vector *ST23 = gsl_vector_alloc(Np);

		//ST11 contains the qL term that comes from overland flow
		gsl_vector *ST11 = gsl_vector_calloc(Np);

		// ST12 contains term that takes flooding into account
		gsl_vector *ST12 = gsl_vector_calloc(Np);

		int begNode = k*Np;
		for (int i = 0; i < Np; i++)
		{
			double Aval = Chan->A[begNode+i+1];
			double Qval = Chan->Q[begNode+i+1];
			double qL = Chan->qL[begNode+i];
			double qM = Chan->qM[begNode+i];
			double bval = Chan->NodalB[begNode+i];
			double S0 = Chan->dz[begNode+i];
			double m1val = Chan->Nodalm1[begNode+i];
			double m2val = Chan->Nodalm2[begNode+i];
			double dm1val = Chan->dm1[begNode+i];
			double dm2val = Chan->dm2[begNode+i];
			double dbval = Chan->db[begNode+i];
			double nval = Chan->NodalnFriction[begNode+i];
			double I1val = getI1(Aval, bval, m1val, m2val);
			double I2val = getI2(Aval, bval, dbval, m1val, dm1val, m2val, dm2val);
			double Sfval = getS_f(Aval, Qval, bval, m1val, m2val, nval);
			double beta = Chan->beta[begNode+i];
			double localG;
#ifdef WDON
			if(Chan->WD[k]==1)
				localG = g;
			else
				localG = 0;
#else
			localG = g;
#endif

			gsl_vector_set(F1, i, Qval);
			gsl_vector_set(F2, i, beta*Qval*Qval/Aval + localG*I1val);
			gsl_vector_set(ST21, i, localG*I2val);
			gsl_vector_set(ST22, i, localG*Aval*S0);
			gsl_vector_set(ST23, i, localG*Aval*Sfval);
			gsl_vector_set(ST11, i, qL);
			gsl_vector_set(ST12, i, qM);

		}

		//gsl_blas_dgemv(CblasNoTrans, 2.0/h, InvM, ST, 1.0, ST11);

		gsl_vector *localFhat1 = gsl_vector_alloc(2);
		gsl_vector_set(localFhat1, 0, Fhat1R[k]);
		gsl_vector_set(localFhat1, 1, Fhat1L[k+1]);

		gsl_vector *localFhat2 = gsl_vector_alloc(2);
		gsl_vector_set(localFhat2, 0, Fhat2R[k]);
		gsl_vector_set(localFhat2, 1, Fhat2L[k+1]);

		// cacluate the volume integral of the flux
		gsl_vector *localRHS1 = gsl_vector_calloc(Np);
		gsl_vector *localRHS2 = gsl_vector_calloc(Np);
		gsl_blas_dgemv(CblasNoTrans, 2.0/h, VolMat, F1, 1.0, localRHS1);
		gsl_blas_dgemv(CblasNoTrans, 2.0/h, VolMat, F2, 1.0, localRHS2);

		// calculate the surface integral of the flux
		gsl_vector *SurfPart1 = gsl_vector_calloc(Np);
		gsl_vector *SurfPart2 = gsl_vector_calloc(Np);
		gsl_blas_dgemv(CblasNoTrans, 2.0/h, LIFT, localFhat1, 1.0, SurfPart1);
		gsl_blas_dgemv(CblasNoTrans, 2.0/h, LIFT, localFhat2, 1.0, SurfPart2);

		// calculate the RHS
		gsl_vector_add(localRHS1, SurfPart1);
		gsl_vector_add(localRHS1, ST11);
		gsl_vector_add(localRHS1, ST12);
		gsl_vector_add(localRHS2, SurfPart2);
		gsl_vector_add(localRHS2, ST21);
		gsl_vector_add(localRHS2, ST22);
		gsl_vector_sub(localRHS2, ST23);

		for(int i = 0; i < Np; i++)
		{
			RHSA[begNode+i+1] = gsl_vector_get(localRHS1, i);
			RHSQ[begNode+i+1] = gsl_vector_get(localRHS2, i);
		}

		gsl_vector_free(F1);
		gsl_vector_free(F2);
		gsl_vector_free(localFhat1);
		gsl_vector_free(localFhat2);
		gsl_vector_free(SurfPart1);
		gsl_vector_free(SurfPart2);
		gsl_vector_free(ST11);
		gsl_vector_free(ST12);
		gsl_vector_free(ST21);
		gsl_vector_free(ST22);
		gsl_vector_free(ST23);
		gsl_vector_free(localRHS1);
		gsl_vector_free(localRHS2);

	}

	free(Fhat1L);
	free(Fhat1R);
	free(Fhat2L);
	free(Fhat2R);

}

void computeLKinematicEls(double time, double* RHS, int fp)
{
	int NumEl = FloodplainList[fp]->NumEl;
	// calculate and store the flux (Q) at the downstream edge of each kinematic element
	// these fluxes will be used to calculate the upwinded flux values
	double *Fhat = xcalloc(NumEl, sizeof(double));
	for (int i = 0; i < NumEl; ++i)
	{
		struct kinematicEl *kinEl = KinematicElList[i];
		if (kinEl->isActive)
		{
			int Np = kinEl->Np;
			double myA = kinEl->A[Np-1];
			double myS0 = kinEl->dz[Np-1];
			double myNf = kinEl->NodalnFriction[Np-1];
			double myWeq = kinEl->weq;
			double myH = myA/myWeq;

			// using manning's N.
			Fhat[i] = myWeq*sqrt(myS0)*pow(myH, 5.0/3)/myNf;

			// using chezy's relationship. nf stands for C, chezy's coefficient
			//Fhat[i] = myWeq*sqrt(myS0*myH)*myH*myNf;
		}
	}

	// now calculate the right hand side
	for (int i = 0; i < NumEl; ++i)
	{
		struct kinematicEl *kinEl = KinematicElList[i];
		if (kinEl->isActive)
		{
			double h_el = kinEl->dh;
			int Np = kinEl->Np;

			gsl_vector *F = gsl_vector_calloc(Np);

			gsl_vector *ST = gsl_vector_calloc(Np);			 // will eventually store R-I (Rainfall - Infiltration)

			double weq = kinEl->weq;

			for (int j = 0; j < Np; ++j)
			{
				double A = kinEl->A[j];
				double nf = kinEl->NodalnFriction[j];
				double S0 = kinEl->dz[j];
				double H = A/weq;

				// using manning's N
				double myF = weq*sqrt(S0)*pow(H,5.0/3)/nf;

				// using Chezy's relationship (nf stands for C)
				//double myF = weq*sqrt(S0*H)*H*nf;

				if (S0 < 0 )
					printf("z1 = %lf z2 = %lf\n", kinEl->z1, kinEl->z2);
				if (isnan(myF))
				{
					printf("myF is nan; A = %lf, nf = %lf, S0 = %lf, H = %lf weq = %lf, kinElNum = %d\n", A, nf, S0, H, weq, i);
					exit(1);
				}
				gsl_vector_set(F, j, myF);

				double rainfall;
				if (time < 1800)
					rainfall = 1.0/12/3600;

				gsl_vector_set(ST, j, weq*rainfall);
				// second parking lot case
				//if (time  <= 180)
				//	gsl_vector_set(ST, j, weq*2.0/12/3600);
				//else if (time <= 360)
				//	gsl_vector_set(ST, j, weq*4.0/12/3600);

			}
			// Now calculate total upstream flux.
			// If the element is at the boundary of the watershed, flux coming in will be 0.
			int numUpstreamEls = kinEl->numUpstreamEls;
			double Fhat1 = 0.0;
			//if (numUpstreamEls > 1)
			//	printf(" i = %d UpEl1 = %d UpEl2 = %d\n", i, kinEl->upstreamEls[0], kinEl->upstreamEls[1]);
			for (int j = 0; j < numUpstreamEls; ++j)
			{
				int upstreamEl = kinEl->upstreamEls[j];
				//printf("upstream el = %d\t", upstreamEl);
				Fhat1 += Fhat[upstreamEl];
			}
			//printf("downstream el = %d\n", kinEl->el2);
			double Fhat2 = Fhat[i];
			gsl_vector *localFhat = gsl_vector_alloc(2);
			gsl_vector_set(localFhat, 0, Fhat1);
			gsl_vector_set(localFhat, 1, Fhat2);

			gsl_vector *localRHS = gsl_vector_calloc(Np);

			// calculate the volume integral
			gsl_blas_dgemv(CblasNoTrans, 2.0/h_el, VolMat, F, 1.0, localRHS);

			// calculate the surface integral
			gsl_vector *SurfPart = gsl_vector_calloc(Np);
			gsl_blas_dgemv(CblasNoTrans, 2.0/h_el, LIFT, localFhat, 1.0, SurfPart);

			// subtract the surface integral from the volume integral
			gsl_vector_add(localRHS, SurfPart);

			// add source term to the right hand side

			gsl_vector_add(localRHS, ST);

			for (int j = 0; j < Np; j++)
			{
				RHS[i*Np+j] = gsl_vector_get(localRHS, j);
				if (isnan(RHS[i*Np+j]))
				{
					printf("SurfPart = %lf \n", gsl_vector_get(SurfPart,j));
				}
			}
			gsl_vector_free(F);
			gsl_vector_free(localFhat);
			gsl_vector_free(SurfPart);
			gsl_vector_free(localRHS);
			gsl_vector_free(ST);

		}

	}
	free(Fhat);

}

/***********************************************************************************************//**
 * Function for evaluating the right hand side of the discrete ODE obtained from the DG discretization
 * of the 2-D shallow water equations
 * @param [in] currRegion a pointer to the junction structure corresponding to the junction that is
 * currently being worked on
 * @param [in] time  a double representing the current time of the simulation
 * @param [out] RHSZeta a pointer to an array of size NumEl x 3 in which the right hand side for
 * the water surface elevation will be stored
 * @param [out] RHSQx a pointer to an array of size NumEl x 3 in which the right hand side for the
 * momentum in the x-direction is stored
 * @param [out] RHSQy a pointer to an array of size NumEl x 3 in which the right hand side for the
 * momentum in the y-direction is stored
 *
 ***************************************************************************************************/

void compute2DL(struct TwoDRegion *currRegion, double time, double *RHSZeta, double *RHSQx, double *RHSQy, double dt)
{

	int NumEdges = currRegion->TotalNumEdges;
	int NumEl = currRegion->NumEl;
	int Nfp = currRegion->P + 1;
	int Np = currRegion->Np;

#ifdef WDON
	/**** Compute the mass over an element to use later for ensuring ********/
	/**** positive mass with the flux in a wetting and drying treatment *************/
	double* mass = xcalloc(NumEl, sizeof(double));
	gsl_vector *height = gsl_vector_calloc(Np);
	gsl_vector *tmp = gsl_vector_alloc(Np);
	gsl_vector *jac = gsl_vector_alloc(Np);

	for (int i = 0; i < NumEl; ++i)
	{
		for (int j = 0; j < Np; j++)
		{
			gsl_vector_set(height, j, currRegion->zeta[i][j] + currRegion->NodalZ[i][j]);
			gsl_vector_set(jac, j, currRegion->jac[i]);
		}

		gsl_blas_dgemv(CblasNoTrans, 1.0, MassMatrix2D, height, 0.0, tmp);
		gsl_blas_ddot(jac, tmp, &mass[i]);

	}

	gsl_vector_free(height);
	gsl_vector_free(tmp);
	gsl_vector_free(jac);
#endif

	/********************************* Compute Roe's flux at the face **********************************************/
	double **Fhat1dotn, **Fhat2dotn, **Fhat3dotn;
	Fhat1dotn = malloc(NumEl*sizeof(double*));
	Fhat2dotn = malloc(NumEl*sizeof(double*));
	Fhat3dotn = malloc(NumEl*sizeof(double*));
	for (int i = 0; i < NumEl; i++)
	{
		Fhat1dotn[i] = xcalloc(3*Nfp, sizeof(double));
		Fhat2dotn[i] = xcalloc(3*Nfp, sizeof(double));
		Fhat3dotn[i] = xcalloc(3*Nfp, sizeof(double));
	}

	for (int i=0; i<NumEdges; ++i)
	{
		int el1 = currRegion->EdgtoEls[i*2];
		int el2 = currRegion->EdgtoEls[i*2+1];

		int ledg1 = currRegion->GlobaltoLocalEdg[i*2];
		int ledg2 = currRegion->GlobaltoLocalEdg[i*2+1];

		// take the normal from element 1 side
		double nx = currRegion->nx[el1*3+ledg1];
		double ny = currRegion->ny[el1*3+ledg1];

		double tx = -ny;
		double ty = nx;

		for (int j = 0; j < Nfp; j++)
		{
			double zeta_in, Qx_in, Qy_in;
			double zeta_ex, Qx_ex, Qy_ex;
			double z_in, z_ex;			// should be the same as z_ex

			int lv1 = currRegion->GlobalEdgPosNegNodes[i][2*j];
			int lv2 = currRegion->GlobalEdgPosNegNodes[i][2*j+1];

			int pos1 = currRegion->PosInFVec[i][2*j];
			int pos2 = currRegion->PosInFVec[i][2*j+1];

			double Fn_in[3], Fn_ex[3];

			zeta_in = currRegion->zeta[el1][lv1];
			Qx_in = currRegion->Qx[el1][lv1];
			Qy_in = currRegion->Qy[el1][lv1];
			z_in = currRegion->NodalZ[el1][lv1];

			double Q_T_in = Qx_in*tx + Qy_in*ty;

			// need to change this later so that the boundary condition is given per node on the edge
			// if the edge is an exterior edge connected to a channel
			int bdrypres = currRegion->BdryPrescribed[i];

		//if (el1 == 531 || el2 == 531)
		//{
		//	printf("el1 = %d, el2 = %d\n", el1, el2);
		//	printf("WD1 = %d, WD2 = %d\n", currRegion->WD[el1], currRegion->WD[el2]);
		//	printf("bdrypres = %d\n", bdrypres);
		//}

			// manufactured solution
			if (bdrypres == 555)
				//if (bdrypres == 555 || (el1 == el2))
			{
				double xval = currRegion->NodalX[el1][lv1];
				double yval = currRegion->NodalY[el1][lv1];
				zeta_ex = getmanH(xval, yval, time);
				Qx_ex = getQx(xval, yval, time);
				Qy_ex = getQy(xval, yval, time);
			}
			// if inflow or outflow boundary, i.e. if bdrypres = 1 or 2
			//for floodplains
			else if (bdrypres == 111)
			{
				zeta_ex = currRegion->bzeta[i];
				Qx_ex = Qx_in;
				Qy_ex = Qy_in;
			}

			else if (bdrypres == 222)
			{
				zeta_ex = zeta_in;
				double Q_N_ex = currRegion->bQn[i];
				double Q_T_ex = Q_T_in;
				double denom = 1./(nx*ty-ny*tx);
				Qx_ex = (ty*Q_N_ex - ny*Q_T_ex)*denom;
				Qy_ex = (-tx*Q_N_ex + nx*Q_T_ex)*denom;
			}

			else if (bdrypres == 333)
			{
				zeta_ex = currRegion->bQn[i];
				double Q_N_ex = currRegion->bQn[i];
				double Q_T_ex = Q_T_in;
				double denom = 1./(nx*ty-ny*tx);
				Qx_ex = (ty*Q_N_ex - ny*Q_T_ex)*denom;
				Qy_ex = (-tx*Q_N_ex + nx*Q_T_ex)*denom;

			}
			// for junctions
			else if (bdrypres == 1 || bdrypres == 2)
			{
				zeta_ex = currRegion->bzeta[i];
				double Q_N_ex = currRegion->bQn[i];
				// for the first iteration
				if (zeta_ex == -10000.0 && Q_N_ex == -10000.0)
				{
					zeta_ex = zeta_in;
					Qx_ex = Qx_in;
					Qy_ex = Qy_in;
					z_ex = z_in;
				}
				else
				{
					double Q_T_ex = Q_T_in;
					double denom = 1./(nx*ty-ny*tx);
					Qx_ex = (ty*Q_N_ex - ny*Q_T_ex)*denom;
					Qy_ex = (-tx*Q_N_ex + nx*Q_T_ex)*denom;
					z_ex = z_in;
				}
			}

			// for floodplains
			else if (bdrypres == 3)
			{
				double Q_N_ex = receive_floodwater(currRegion, i);

				// element 1
				Qx_in = currRegion->Qx[el1][lv1];
				Qy_in = currRegion->Qy[el1][lv1];
				zeta_in = currRegion->zeta[el1][lv1];
				z_in = currRegion->NodalZ[el1][lv1];
				zeta_ex = zeta_in;
				Q_T_in = Qx_in*tx + Qy_in*ty;
				double Q_T_ex = Q_T_in;
				double denom = 1./(nx*ty-ny*tx);
				Qx_ex = (ty*Q_N_ex - ny*Q_T_ex)*denom;
				Qy_ex = (-tx*Q_N_ex + nx*Q_T_ex)*denom;

				double localG = g;

#ifdef WDON
				if (currRegion->WD[el1] == 0)
					localG = 0;
				else
					localG = g;
#endif

				double current_max_lam= RoeFluxJunc(zeta_in, zeta_ex, Qx_in, Qx_ex, Qy_in, Qy_ex, z_in, nx, ny, localG,Fn_in);

				//if (el1 == 170 && fabs(Q_N_ex) > 0)
				//{
				//	printf("***************\n");
				//	printf("time = %lf\n", time);
				//	printf("j = %d el1 = %d, el2 = %d\n", j, el1, el2);
				//	printf("WD1 = %d, WD2 = %d\n", currRegion->WD[el1], currRegion->WD[el2]);
				//	printf("Q_N_ex = %lf, Qx_in = %lf, Qy_in = %lf, \n", Q_N_ex, Qx_in, Qy_in);
				//	printf("Fn_in = %lf, %lf, %lf\n", Fn_in[0], Fn_in[1], Fn_in[2]);
				//}

				// element 2
				nx = -nx;
				ny = -ny;
				tx = -ny;
				ty = nx;
				denom = 1./(nx*ty-ny*tx);
				zeta_in = currRegion->zeta[el2][lv2];
				z_in = currRegion->NodalZ[el2][lv2];
				zeta_ex = zeta_in;
				Qx_in = currRegion->Qx[el2][lv2];
				Qy_in = currRegion->Qy[el2][lv2];
				Q_T_in = Qx_in*tx + Qy_in*ty;
				Q_T_ex = Q_T_in;
				denom = 1./(nx*ty-ny*tx);
				Qx_ex = (ty*Q_N_ex - ny*Q_T_ex)*denom;
				Qy_ex = (-tx*Q_N_ex + nx*Q_T_ex)*denom;
				z_ex = z_in;

#ifdef WDON
				if (currRegion->WD[el2] == 0)
					localG = 0;
				else
					localG = g;
#endif

				current_max_lam= RoeFluxJunc(zeta_in, zeta_ex, Qx_in, Qx_ex, Qy_in, Qy_ex, z_in, nx, ny, localG, Fn_ex);

				if (el2 == 170 && fabs(Q_N_ex)>0)
				{
					printf("***************\n");
					printf("el1 = %d, el2 = %d\n", el1, el2);

					printf("Q_N_ex = %lf\n", Q_N_ex);
					printf("Fn_in = %lf, %lf, %lf\n", Fn_in[0], Fn_in[1], Fn_ex[2]);
				}
			}

			// if edge i is a boundary edge but not connected to a channel, implement no flux boundary condition
			else if (el1 == el2)
			{
				zeta_ex  = currRegion->zeta[el2][lv2];
				// Compute the velocity in the normal direction
				double Q_N_in = Qx_in*nx + Qy_in*ny;
				double Q_T_in = Qx_in*tx + Qy_in*ty;

				// Reflect the velocity in the normal direction
				double Q_N_ex = -Q_N_in;
				double Q_T_ex = Q_T_in;

				// Compute the x and y components of the external state flow
				double denom = 1./(nx*ty - ny*tx);
				Qx_ex = (ty*Q_N_ex - ny*Q_T_ex)*denom;
				Qy_ex = (-tx*Q_N_ex + nx*Q_T_ex)*denom;

				z_ex = z_in;

			}

			// if the edge is not a boundary edge
			else
			{
				zeta_ex = currRegion->zeta[el2][lv2];
				Qx_ex = currRegion->Qx[el2][lv2];
				Qy_ex = currRegion->Qy[el2][lv2];
				z_ex = currRegion->NodalZ[el2][lv2];
			}

#ifdef WDON
			// Check to see if both of the elements separated by this edge are dry
			if (currRegion->WD[el1] == 0 && currRegion->WD[el2] == 0 && (bdrypres != 3) && (bdrypres != 222) && (bdrypres != 2) && (bdrypres !=1))
			{
				Fn_in[0] = 0.0;
				Fn_in[1] = 0.0;
				Fn_in[2] = 0.0;
				Fn_ex[0] = 0.0;
				Fn_ex[1] = 0.0;
				Fn_ex[2] = 0.0;

				//// Reflection flux for the interior element
				//double zeta_ex_ref = zeta_in;
				//double Q_N_in = Qx_in*nx + Qy_in*ny;
				//double Q_T_in = Qx_in*tx + Qy_in*ty;

				//double Q_N_ex = -Q_N_in;
				//double Q_T_ex = Q_T_in;
				//
				//double denom = 1./(nx*ty - ny*tx);
				//double Qx_ex_ref = (ty*Q_N_ex - ny*Q_T_ex)*denom;
				//double Qy_ex_ref = (-tx*Q_N_ex + nx*Q_T_ex)*denom;

				//double max_lam_in = RoeFluxJunc(zeta_in, zeta_ex_ref, Qx_in,Qx_ex_ref,
				//	Qy_in, Qy_ex_ref, z_in, nx, ny, 0, Fn_in);

				//// Reflection flux for the exterior element
				//double zeta_in_ref = zeta_ex;
				//Q_N_ex = Qx_ex*nx + Qy_ex*ny;
				//Q_T_ex = Qx_ex*tx + Qy_ex*ty;

				//Q_N_in = -Q_N_ex;
				//Q_T_in = Q_T_ex;
				//
				//double Qx_in_ref = (ty*Q_N_in - ny*Q_T_in)*denom;
				//double Qy_in_ref = (-tx*Q_N_in + nx*Q_T_ex)*denom;
				//double max_lam_ex = RoeFluxJunc(zeta_in_ref, zeta_ex, Qx_in_ref, Qx_ex, Qy_in_ref, Qy_ex, z_in, nx, ny, 0, Fn_ex);

				//if (i ==0)
				//	currRegion->max_lambda = max(max_lam_in, max_lam_ex);
				//else
				//{
				//	currRegion->max_lambda = fmax(currRegion->max_lambda, max_lam_in);
				//	currRegion->max_lambda = fmax(currRegion->max_lambda, max_lam_ex);
				//}

			}
			else if (bdrypres != 3 && bdrypres != 222)// if the elements aren't both dry
			{
				double Fhatdotn[3];
				double current_max_lam= RoeFluxJunc(zeta_in, zeta_ex, Qx_in, Qx_ex, Qy_in, Qy_ex, z_in, nx, ny, g,Fhatdotn);

				if (isnan(Fhatdotn[0]) || isnan(Fhatdotn[1]) || isnan(Fhatdotn[2]))
				{
					printf("2D both elements wet flux not a number, edge %d, time %e, for domain type %d \n",i,time, currRegion->type);
					printf("ze_ex = %lf, ze_in = %lf, z_in = %lf, Qx_ex = %lf, Qx_in = %lf, Qy_ex = %lf, Qy_in = %lf, el_ex = %d, el_in = %d, bdrypres = %d\n", zeta_ex, zeta_in, z_in, Qx_ex, Qx_in, Qy_ex, Qy_in, el2, el1, bdrypres);
					printf("wdfalg1 = %d , wdflag2 = %d\n", currRegion->WD[el1], currRegion->WD[el2]);
					exit(EXIT_FAILURE);
				}

				// Check to see if the flux is large enough to dry up the elements
				// Calculate the sum of the length of the three edges of the interior and exterior element
				double edg_len_el1 = 0;
				for (int v = 0; v < 3; v++)
				{
					int v1 = currRegion->EltoVert[el1*3 + v];
					int v2 = currRegion->EltoVert[el1*3 + (v+1)%3];
					edg_len_el1 += sqrt(pow(currRegion->Vx[v1] - currRegion->Vx[v2], 2) + pow(currRegion->Vy[v1] - currRegion->Vy[v2],2));
				}

				double edg_len_el2 = 0;
				for (int v = 0; v < 3; v++)
				{
					int v1 = currRegion->EltoVert[el2*3 + v];
					int v2 = currRegion->EltoVert[el2*3 + (v+1)%3];
					edg_len_el2 += sqrt(pow(currRegion->Vx[v1] - currRegion->Vx[v2], 2) + pow(currRegion->Vy[v1] - currRegion->Vy[v2],2));
				}

				double maxBetaOverAlpha = 2;

				if ((1.01*Fhatdotn[0]*edg_len_el1*maxBetaOverAlpha*dt >= mass[el1]) || (-1.01*Fhatdotn[0]*edg_len_el2*maxBetaOverAlpha*dt >= mass[el2]))
				{
					//printf("either el %d or el %d will be dry \n", el1, el2);
					Fn_in[0] = 0.0;
					Fn_in[1] = 0.0;
					Fn_in[2] = 0.0;
					Fn_ex[0] = 0.0;
					Fn_ex[1] = 0.0;
					Fn_ex[2] = 0.0;

				}

				else // check to make sure the flux is not coming from a dry element
				{
					if (currRegion->WD[el1] == 1 || (el1 == el2))
					{
						for (int j =0; j < 3; ++j)
						{
							Fn_in[j] = Fhatdotn[j];
						}
					}
					else if (Fhatdotn[0] > 0)
					{
						//printf("initially, el1 = %d, j = %d, Fn_ex = %lf\n", el1, j, Fhatdotn[0]);
						zeta_ex = currRegion->zeta[el2][lv2];
						zeta_in = zeta_ex;
						Qx_ex = currRegion->Qx[el2][lv2];
						Qy_ex = currRegion->Qy[el2][lv2];
						double Q_N_ex = Qx_ex*nx + Qy_ex*ny;
						double Q_N_in = -Q_N_ex;
						double Q_T_ex = Qx_ex*tx + Qy_ex*ty;
						Q_T_in = Q_T_ex;
						double denom = 1./(nx*ty-ny*tx);
						Qx_in = (ty*Q_N_in - ny*Q_T_in)*denom;
						Qy_in = (-tx*Q_N_in + nx*Q_T_in)*denom;

						double max_lam_in = RoeFluxJunc(zeta_in, zeta_ex, Qx_in, Qx_ex, Qy_in, Qy_ex,
								z_ex, nx, ny, g, Fn_in);
						if (isnan(Fn_in[0]) || isnan(Fn_in[1]) || isnan(Fn_in[2]))
						{
							printf("2D interior element dry flux not a number, edge %d, el_in = %d, el_ex = %d, domain type = %d\n",i, el1, el2, currRegion->type);
							printf("ze_ex = %lf, ze_in = %lf, z_in = %lf, z_ex = %lf, Qx_ex = %lf, Qx_in = %lf, Qy_ex = %lf, Qy_in = %lf, el_ex = %d, el_in = %d, bdrypres = %d\n", zeta_ex, zeta_in, z_in, z_ex, Qx_ex, Qx_in, Qy_ex, Qy_in, el2, el1, bdrypres);
							printf("wdfalg1 = %d , wdflag2 = %d\n", currRegion->WD[el1], currRegion->WD[el2]);
							exit(EXIT_FAILURE);
						}
						//printf("after reflection, el1 = %d, Fn_ex = %lf\n", el1, Fn_in[0]);
						current_max_lam = max(current_max_lam, max_lam_in);
					}

					else
					{
						double max_lam_in = RoeFluxJunc(zeta_in, zeta_ex, Qx_in, Qx_ex, Qy_in, Qy_ex,
								z_in, nx, ny, 0, Fn_in);

					}

					if (currRegion->WD[el2] == 1 && (el1 != el2))
					{
						for (int j = 0; j < 3; ++j)
						{
							Fn_ex[j] = Fhatdotn[j];
						}
					}
					else if ((el1 != el2) && (Fhatdotn[0] < 0))
					{
					//	printf("initially, el2 = %d, Fn_ex = %lf\n", el2, -Fhatdotn[0]);
						Qx_in = currRegion->Qx[el1][lv1];
						Qy_in = currRegion->Qy[el1][lv1];
						zeta_in = currRegion->zeta[el1][lv1];
						double Q_N_in = Qx_in*nx + Qy_in*ny;
						double Q_T_in = Qx_in*tx + Qy_in*ty;
						double Q_N_ex = -Q_N_in;
						double Q_T_ex = Q_T_in;
						double denom = 1.0/(nx*ty - ny*tx);
						Qx_ex = (ty*Q_N_ex - ny*Q_T_ex)*denom;
						Qy_ex = (-tx*Q_N_ex + nx*Q_T_ex)*denom;
						zeta_ex = zeta_in;

						double max_lam_ex = RoeFluxJunc(zeta_in, zeta_ex, Qx_in, Qx_ex,
								Qy_in, Qy_ex, z_in, nx, ny, g, Fn_ex);
						current_max_lam = max(current_max_lam, max_lam_ex);

						if (isnan(Fn_ex[0]) || isnan(Fn_ex[1]) || isnan(Fn_ex[2]))
						{
							printf("2D exterior element dry flux not a number, edge %d \n",i);
							exit(EXIT_FAILURE);
						}
						//printf("el2 = %d, Fn_ex = %lf\n", el2, -Fn_ex[0]);

					//	printf("after reflection, el2 = %d, Fn_ex = %lf\n", el2, -Fn_ex[0]);
					}
					else if (el1 != el2 )
					{
						double max_lam_ex = RoeFluxJunc(zeta_in, zeta_ex, Qx_in, Qx_ex,
								Qy_in, Qy_ex, z_in, nx, ny, 0, Fn_ex);
						current_max_lam = max(current_max_lam, max_lam_ex);

					}

					if (i == 0)
						currRegion->max_lambda = current_max_lam;
					else
						currRegion->max_lambda = fmax(currRegion->max_lambda, current_max_lam);
				}

			}

#else

			double Fhatdotn[3];
			double current_max_lam= RoeFluxJunc(zeta_in, zeta_ex, Qx_in, Qx_ex, Qy_in, Qy_ex, z_in, nx, ny, g, Fhatdotn);

			if (isnan(Fhatdotn[0]) || isnan(Fhatdotn[1]) || isnan(Fhatdotn[2]))
			{
				printf("2D numerical flux not a number, edge %d \n",i);
				printf("zeta_in = %e, zeta_ex = %e, z_edge = %e, BdryPrescribed = %d \n", zeta_in, zeta_ex, z_in, bdrypres);
				exit(EXIT_FAILURE);
			}

			for (int j = 0; j < 3; ++j)
			{
				Fn_in[j] = Fhatdotn[j];
				Fn_ex[j] = Fhatdotn[j];
			}

			if (i == 0)
				currRegion->max_lambda = current_max_lam;
			else
				currRegion->max_lambda = max(currRegion->max_lambda, current_max_lam);

#endif

			// store Fhatdotn for the two elements connected by this edge
			Fhat1dotn[el1][pos1] = Fn_in[0];
			Fhat2dotn[el1][pos1] = Fn_in[1];
			Fhat3dotn[el1][pos1] = Fn_in[2];

			// store the value for the exterior element, only if the edge is not a boundary edge
			if (el2 != el1 && (bdrypres != 3))
			{
				Fhat1dotn[el2][pos2] = -Fn_ex[0];
				Fhat2dotn[el2][pos2] = -Fn_ex[1];
				Fhat3dotn[el2][pos2] = -Fn_ex[2];
			}
			else if (el2 != el1)  // for bdyrpes = 3, we have already taken sign of normals into account during flux calculation
			{
				Fhat1dotn[el2][pos2] = Fn_ex[0];
				Fhat2dotn[el2][pos2] = Fn_ex[1];
				Fhat3dotn[el2][pos2] = Fn_ex[2];
			}

		} // end node loop

	}	// end edge loop

	for (int k=0; k<NumEl; ++k)
	{
		gsl_vector *F1x = gsl_vector_alloc(Np);
		gsl_vector *F2x = gsl_vector_alloc(Np);
		gsl_vector *F3x = gsl_vector_alloc(Np);
		gsl_vector *F1y = gsl_vector_alloc(Np);
		gsl_vector *F2y = gsl_vector_alloc(Np);
		gsl_vector *F3y = gsl_vector_alloc(Np);
		gsl_vector *ST21 = gsl_vector_calloc(Np);
		gsl_vector *ST22 = gsl_vector_calloc(Np);
		gsl_vector *ST31 = gsl_vector_calloc(Np);
		gsl_vector *ST32 = gsl_vector_calloc(Np);
		//gsl_vector *ST11 = gsl_vector_calloc(Np);

		double localG = g;
#ifdef WDON
		if(currRegion->WD[k] == 0)
			localG = 0;
#endif

		for (int i = 0; i < Np; i++)
		{
			double zeta = currRegion->zeta[k][i];
			double Qx = currRegion->Qx[k][i];
			double Qy = currRegion->Qy[k][i];
			double z = currRegion->NodalZ[k][i];
			double H = zeta + z;
			double f2x = Qx*Qx/H + 0.5*localG*(H*H - z*z);
			double f2y = Qx*Qy/H;
			double f3x = f2y;
			double f3y = Qy*Qy/H + 0.5*localG*(H*H - z*z);
			gsl_vector_set(F1x, i, Qx);
			gsl_vector_set(F1y, i, Qy);
			gsl_vector_set(F2x, i, f2x);
			gsl_vector_set(F2y, i, f2y);
			gsl_vector_set(F3x, i, f3x);
			gsl_vector_set(F3y, i, f3y);

			double u = Qx/H;
			double v = Qy/H;
			double nfric = currRegion->NodalnFriction[k][i];
			double tau = localG*nfric*nfric*sqrt(u*u + v*v)/pow(H,4.0/3);
			double dzx = currRegion->Nodaldzx[k][i];
			double dzy = currRegion->Nodaldzy[k][i];

			gsl_vector_set(ST21, i, localG*zeta*dzx);
			gsl_vector_set(ST22, i, -tau*Qx);
			gsl_vector_set(ST31, i, localG*zeta*dzy);
			gsl_vector_set(ST32, i, -tau*Qy);

			// Source terms for manufactured solution
			//double xval = currRegion->NodalX[k][i];
			//double yval = currRegion->NodalY[k][i];

			//double st2 = getS2(xval, yval, time);
			//double st3 = getS3(xval, yval, time);
			//gsl_vector_set(ST21, i, st2);
			//gsl_vector_set(ST31, i, st3);

#ifdef WDON
			if (currRegion->WD[k] == 0)
			{
				gsl_vector_set(F1x, i, 0);
				gsl_vector_set(F1y, i, 0);
				gsl_vector_set(F2x, i, 0);
				gsl_vector_set(F2y, i, 0);
				gsl_vector_set(F3x, i, 0);
				gsl_vector_set(F3y, i, 0);
				gsl_vector_set(ST21, i, 0);
				gsl_vector_set(ST22, i, 0);
				gsl_vector_set(ST31, i, 0);
				gsl_vector_set(ST32, i, 0);
			}
#endif

		// rainfall
		//if (currRegion->type == 2)
		//	gsl_vector_set(ST11, i, 0.5*12/3600);

		}

		// Calculate the volume integral of the flux
		double rx = currRegion->rx[k];
		double ry = currRegion->ry[k];
		double sx = currRegion->sx[k];
		double sy = currRegion->sy[k];

		gsl_vector *localRHS1 = gsl_vector_calloc(Np);
		gsl_vector *localRHS2 = gsl_vector_calloc(Np);
		gsl_vector *localRHS3 = gsl_vector_calloc(Np);

		gsl_vector *lR12 = gsl_vector_calloc(Np);
		gsl_vector *lR13 = gsl_vector_calloc(Np);
		gsl_vector *lR14 = gsl_vector_calloc(Np);
		gsl_vector *lR22 = gsl_vector_calloc(Np);
		gsl_vector *lR23 = gsl_vector_calloc(Np);
		gsl_vector *lR24 = gsl_vector_calloc(Np);
		gsl_vector *lR32 = gsl_vector_calloc(Np);
		gsl_vector *lR33 = gsl_vector_calloc(Np);
		gsl_vector *lR34 = gsl_vector_calloc(Np);

		gsl_blas_dgemv(CblasNoTrans, rx, Drw, F1x, 0.0, localRHS1);
		gsl_blas_dgemv(CblasNoTrans, sx, Dsw, F1x, 0.0, lR12);
		gsl_blas_dgemv(CblasNoTrans, ry, Drw, F1y, 0.0, lR13);
		gsl_blas_dgemv(CblasNoTrans, sy, Dsw, F1y, 0.0, lR14);

		gsl_vector_add(localRHS1, lR12);
		gsl_vector_add(localRHS1, lR13);
		gsl_vector_add(localRHS1, lR14);

		gsl_blas_dgemv(CblasNoTrans, rx, Drw, F2x, 0.0, localRHS2);
		gsl_blas_dgemv(CblasNoTrans, sx, Dsw, F2x, 0.0, lR22);
		gsl_blas_dgemv(CblasNoTrans, ry, Drw, F2y, 0.0, lR23);
		gsl_blas_dgemv(CblasNoTrans, sy, Dsw, F2y, 0.0, lR24);

		gsl_vector_add(localRHS2, lR22);
		gsl_vector_add(localRHS2, lR23);
		gsl_vector_add(localRHS2, lR24);

		gsl_blas_dgemv(CblasNoTrans, rx, Drw, F3x, 0.0, localRHS3);
		gsl_blas_dgemv(CblasNoTrans, sx, Dsw, F3x, 0.0, lR32);
		gsl_blas_dgemv(CblasNoTrans, ry, Drw, F3y, 0.0, lR33);
		gsl_blas_dgemv(CblasNoTrans, sy, Dsw, F3y, 0.0, lR34);

		gsl_vector_add(localRHS3, lR32);
		gsl_vector_add(localRHS3, lR33);
		gsl_vector_add(localRHS3, lR34);

		// calculate the surface integral of the flux
		gsl_vector *localFhat1dotn = gsl_vector_alloc(3*Nfp);
		gsl_vector *localFhat2dotn = gsl_vector_alloc(3*Nfp);
		gsl_vector *localFhat3dotn = gsl_vector_alloc(3*Nfp);

		for (int i = 0; i < 3; i++)
		{
			double edgJac = currRegion->edgJac[k*3+i];
			for (int j = 0; j < Nfp; j++)
			{
				int index = i*Nfp+j;
				gsl_vector_set(localFhat1dotn, index, edgJac*Fhat1dotn[k][index]);
				gsl_vector_set(localFhat2dotn, index, edgJac*Fhat2dotn[k][index]);
				//printf("edgJac = %lf Fhat = %lf\n", edgJac, Fhat2dotn[k][index]);
				gsl_vector_set(localFhat3dotn, index, edgJac*Fhat3dotn[k][index]);

			}
		}

		double jac = currRegion->jac[k];

		gsl_vector *SurfPart1 = gsl_vector_calloc(Np);
		gsl_vector *SurfPart2 = gsl_vector_calloc(Np);
		gsl_vector *SurfPart3 = gsl_vector_calloc(Np);

		double fac = 1.0/jac;
		gsl_blas_dgemv(CblasNoTrans, fac, LIFT2D, localFhat1dotn, 0.0, SurfPart1);
		gsl_blas_dgemv(CblasNoTrans, fac, LIFT2D, localFhat2dotn, 0.0, SurfPart2);
		gsl_blas_dgemv(CblasNoTrans, fac, LIFT2D, localFhat3dotn, 0.0, SurfPart3);

		// subtract this from the RHS
		gsl_vector_sub(localRHS1, SurfPart1);
		gsl_vector_sub(localRHS2, SurfPart2);
		gsl_vector_sub(localRHS3, SurfPart3);

		// add source and sink terms
		//gsl_vector_add(localRHS1, ST11);
		//
		//gsl_vector_add(localRHS1, ST11);
		gsl_vector_add(localRHS2, ST21);
		gsl_vector_add(localRHS2, ST22);
		gsl_vector_add(localRHS3, ST31);
		gsl_vector_add(localRHS3, ST32);

		int begNode = k*Np;
		for(int i = 0; i < Np; i++)
		{
			RHSZeta[begNode+i] = gsl_vector_get(localRHS1,i);
			RHSQx[begNode+i] = gsl_vector_get(localRHS2,i);
			RHSQy[begNode+i] = gsl_vector_get(localRHS3, i);

		}

		// free all gsl vectors
		gsl_vector_free(localRHS1);
		gsl_vector_free(localRHS2);
		gsl_vector_free(localRHS3);
		gsl_vector_free(localFhat1dotn);
		gsl_vector_free(localFhat2dotn);
		gsl_vector_free(localFhat3dotn);
		gsl_vector_free(SurfPart1);
		gsl_vector_free(SurfPart2);
		gsl_vector_free(SurfPart3);
		gsl_vector_free(lR12);
		gsl_vector_free(lR13);
		gsl_vector_free(lR14);
		gsl_vector_free(lR22);
		gsl_vector_free(lR23);
		gsl_vector_free(lR24);
		gsl_vector_free(lR32);
		gsl_vector_free(lR33);
		gsl_vector_free(lR34);
		gsl_vector_free(F1x);
		gsl_vector_free(F2x);
		gsl_vector_free(F3x);
		gsl_vector_free(F1y);
		gsl_vector_free(F2y);
		gsl_vector_free(F3y);
		//gsl_vector_free(ST11);
		gsl_vector_free(ST21);
		gsl_vector_free(ST22);
		gsl_vector_free(ST31);
		gsl_vector_free(ST32);

	}

	// free allocated space
	for (int i = 0; i < NumEl; i++)
	{
		free(Fhat1dotn[i]);
		free(Fhat2dotn[i]);
		free(Fhat3dotn[i]);
	}
	free(Fhat1dotn);
	free(Fhat2dotn);
	free(Fhat3dotn);

#ifdef WDON
	free(mass);
#endif

}


#ifndef __LLVUTILS_H__
#define __LLVUTILS_H__ 1

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <gsl/gsl_cdf.h>

#include "constants.h"
#include "struct.h"
#include "EOBNRv2HMROMstruct.h"
#include "EOBNRv2HMROM.h"
#include "wip.h"
#include "likelihood.h"
#include "splinecoeffs.h"
#include "LLVFDresponse.h"
#include "LLVnoise.h"

/***************** Structures for parameters *****************/

/* Parameters for the generation of a LLV waveform (in the form of a list of modes) */
typedef struct tagLLVParams {
  double tRef;               /* reference time (s) - GPS time at the frequency representing coalescence */
  double phiRef;             /* reference phase (rad) - phase at the frequency representing coalescence (or at fRef if specified) */
  double m1;                 /* mass of companion 1 (solar masses) */
  double m2;                 /* mass of companion 2 (solar masses) */
  double distance;           /* distance of source (Mpc) */
  double ra;                 /* right ascension of the source (rad) */
  double dec;                /* declination of the source (rad) */
  double inclination;        /* inclination of L relative to line of sight (rad) */
  double polarization;       /* polarization angle (rad) */
  int nbmode;                /* number of modes to generate (starting with 22) - defaults to 5 (all modes) */
} LLVParams;

/* Global parameters for the waveform generation and overlap computation */
typedef struct tagLLVGlobalParams {
  double fRef;               /* reference frequency (Hz, default 0 which is interpreted as Mf=0.14) */
  double minf;               /* Minimal frequency (Hz) - when set to 0 (default), use the first frequency covered by the noise data of the detector */
  double maxf;               /* Maximal frequency (Hz) - when set to 0 (default), use the last frequency covered by the noise data of the detector */
  int setphiRefatfRef;       /* Flag for adjusting the FD phase at phiRef at the given fRef, which depends also on tRef - if false, treat phiRef simply as an orbital phase shift (minus an observer phase shift) (default=1) */
  int nbmodeinj;             /* number of modes to include in the injection (starting with 22) - defaults to 5 (all modes) */
  int nbmodetemp;            /* number of modes to include in the templates (starting with 22) - defaults to 5 (all modes) */
  int tagint;                /* Tag choosing the integrator: 0 for wip (default), 1 for linear integration */
  int tagnetwork;            /* Tag choosing the network of detectors to use */
  int nbptsoverlap;          /* Number of points to use in loglinear overlaps (default 32768) */
  int constL;                /* set all logLikelihood to 0 - allows to sample from the prior for testing */
} LLVGlobalParams;

typedef struct tagLLVPrior {
	double deltaT;             /* width of time prior centered on injected value (s) (default 0.1) */
	double comp_min;           /* minimum component mass (solar masses) (default 4) */
	double comp_max;           /* maximum component mass (solar masses) (default 50) */
	double mtot_min;           /* minimum total mass (solar masses) (default 8) */
	double mtot_max;           /* maximum total mass (solar masses) (default 100) */
	double qmax;               /* maximum asymmetric mass ratio (>=1) (default 12) */
	double dist_min;           /* minimum distance of source (Mpc) (default 1) */
	double dist_max;           /* maximum distance of source (Mpc) (default 1e4) */
  double ra_min;            /* minimum ra (rad, default 0) - for testing */
  double ra_max;            /* maximum ra (rad, default 2pi) - for testing */
  double dec_min;           /* minimum dec (rad, default 0) - for testing */
  double dec_max;           /* maximum dec (rad, default pi) - for testing */
  double phase_min;          /* minimum phase (rad, default 0) - for testing */
  double phase_max;          /* maximum phase (rad, default 2pi) - for testing */
  double pol_min;            /* minimum polarization (rad, default 0) - for testing */
  double pol_max;            /* maximum polarization (rad, default 2pi) - for testing */
  double inc_min;            /* minimum inclination (rad, default 0) - for testing */
  double inc_max;            /* maximum inclination (rad, default pi) - for testing */
  double fix_m1;
  double fix_m2;
  double fix_time;
  double fix_ra;
  double fix_dec;
  double fix_phase;
  double fix_pol;
  double fix_dist;
  double fix_inc;
  int pin_m1;
  int pin_m2;
  int pin_time;
  int pin_ra;
  int pin_dec;
  int pin_phase;
  int pin_pol;
  int pin_dist;
  int pin_inc;
  double snr_target;
  int rescale_distprior;
  int flat_distprior;
} LLVPrior;

typedef struct tagLLVRunParams {
	double eff;                /* target efficiency (default 0.1) */
	double tol;                /* logZ tolerance (default 0.5) */
	int    nlive;              /* number of live points (default 1000) */
	char   outroot[200];       /* output root (default "chains/LLVinference_") */
	int    bambi;              /* run BAMBI? (default 0) */
	int    resume;             /* resume form previous run? (default 0) */
	int    maxiter;            /* max number of iterations (default 0 - ignore) */
	char   netfile[200];       /* NN settings file (default "LLVinference.inp") */
  int    mmodal;             /* use multimodal decomposition ? */
  int    maxcls;             /* max number of modes in multimodal decomposition */
  int    nclspar;            /* number of parameters to use for multimodal decomposition - in the order of the cube */
  double ztol;               /* in multimodal decomposition, modes with lnZ lower than Ztol are ignored */
  int    seed;               /* seed the inference by setting one of the live points to the injection ? */
} LLVRunParams;

/************ Structures for signals and injections ************/

// typedef struct tagLLVSignal
// {
//   struct tagListmodesCAmpPhaseFrequencySeries* LHOSignal;   /* Signal in LHO, in the form of a list of the contribution of each mode */
//   struct tagListmodesCAmpPhaseFrequencySeries* LLOSignal;   /* Signal in LLO, in the form of a list of the contribution of each mode */
//   struct tagListmodesCAmpPhaseFrequencySeries* VIRGOSignal; /* Signal in VIRGO, in the form of a list of the contribution of each mode */
//   double LHOhh;                                             /* Inner product (h|h) for LHO */
//   double LLOhh;                                             /* Inner product (h|h) for LLO */
//   double VIRGOhh;                                           /* Inner product (h|h) for VIRGO */
// } LLVSignal;

typedef struct tagLLVSignalCAmpPhase
{
  struct tagListmodesCAmpPhaseFrequencySeries* LHOSignal;   /* Signal in LHO, in the form of a list of the contribution of each mode */
  struct tagListmodesCAmpPhaseFrequencySeries* LLOSignal;   /* Signal in LLO, in the form of a list of the contribution of each mode */
  struct tagListmodesCAmpPhaseFrequencySeries* VIRGOSignal; /* Signal in VIRGO, in the form of a list of the contribution of each mode */
  double LLVhh;                                             /* Combined Inner product (h|h) for dectectors LHV */
} LLVSignalCAmpPhase;

typedef struct tagLLVInjectionCAmpPhase
{
  struct tagListmodesCAmpPhaseSpline* LHOSplines;   /* Signal in LHO, in the form of a list of splines for the contribution of each mode */
  struct tagListmodesCAmpPhaseSpline* LLOSplines;   /* Signal in LLO, in the form of a list of splines for the contribution of each mode */
  struct tagListmodesCAmpPhaseSpline* VIRGOSplines;   /* Signal in VIRGO, in the form of a list of splines for the contribution of each mode */
  double LLVss;                                   /* Combined Inner product (s|s) for dectectors LHV */
} LLVInjectionCAmpPhase;

typedef struct tagLLVSignalReIm /* We don't store the SNRs here, as we will use -1/2(h-s|h-s) for the likelihood */
{
  struct tagReImFrequencySeries* LHOSignal;   /* Signal in LHO, in the form of a Re/Im frequency series where the modes have been summed */
  struct tagReImFrequencySeries* LLOSignal;   /* Signal in LLO, in the form of a Re/Im frequency series where the modes have been summed */
  struct tagReImFrequencySeries* VIRGOSignal;   /* Signal in VIRGO, in the form of a Re/Im frequency series where the modes have been summed */
} LLVSignalReIm;

typedef struct tagLLVInjectionReIm /* Storing the vectors of frequencies and noise values - We don't store the SNRs here, as we will use -1/2(h-s|h-s) for the likelihood */
{
  struct tagReImFrequencySeries* LHOSignal;   /* Signal in LHO, in the form of a Re/Im frequency series where the modes have been summed */
  struct tagReImFrequencySeries* LLOSignal;   /* Signal in LLO, in the form of a Re/Im frequency series where the modes have been summed */
  struct tagReImFrequencySeries* VIRGOSignal;   /* Signal in VIRGO, in the form of a Re/Im frequency series where the modes have been summed */
  gsl_vector* freq;                            /* Vector of frequencies of the injection (assumed to be the same for LHO, LLO, VIRGO) */
  gsl_vector* noisevaluesLHO;                    /* Vector of noise values on freq LHO */
  gsl_vector* noisevaluesLLO;                    /* Vector of noise values on freq LLO */
  gsl_vector* noisevaluesVIRGO;                  /* Vector of noise values on freq VIRGO */
} LLVInjectionReIm;

/************ Functions for LLV parameters, injection, likelihood, prior ************/

/* Parsing parameters for the generation of a LLV waveform, from the command line */
/* Masses are input in solar masses and distances in Mpc - converted in SI for the internals */
void parse_args_LLV(ssize_t argc, char **argv,
    LLVParams* params,
    LLVGlobalParams* globalparams,
    LLVPrior* prior,
    LLVRunParams* run,
    LLVParams* addparams);

/* Functions to print the parameters of the run in files for reference */
int print_parameters_to_file_LLV(
  LLVParams* params,
  LLVGlobalParams* globalparams,
  LLVPrior* prior,
  LLVRunParams* run);
int print_rescaleddist_to_file_LLV(
  LLVParams* params,
  LLVGlobalParams* globalparams,
  LLVPrior* prior,
  LLVRunParams* run);

/* Initialization and clean-up for LLVSignal structures */
void LLVSignalCAmpPhase_Cleanup(LLVSignalCAmpPhase* signal);
void LLVSignalCAmpPhase_Init(LLVSignalCAmpPhase** signal);
void LLVInjectionCAmpPhase_Cleanup(LLVInjectionCAmpPhase* signal);
void LLVInjectionCAmpPhase_Init(LLVInjectionCAmpPhase** signal);
void LLVSignalReIm_Cleanup(LLVSignalReIm* signal);
void LLVSignalReIm_Init(LLVSignalReIm** signal);
void LLVInjectionReIm_Cleanup(LLVInjectionReIm* signal);
void LLVInjectionReIm_Init(LLVInjectionReIm** signal);

/* Function generating a LLV signal as a list of modes in CAmp/Phase form, from LLV parameters */
int LLVGenerateSignalCAmpPhase(
  struct tagLLVParams* params,                 /* Input: set of LLV parameters of the signal */
  struct tagLLVSignalCAmpPhase* signal);  /* Output: structure for the generated signal */
/* Function generating a LLV injection as a list of modes, given as preinterpolated splines, from LLV parameters */
int LLVGenerateInjectionCAmpPhase(
  struct tagLLVParams* injectedparams,    /* Input: set of LLV parameters of the signal */
  struct tagLLVInjectionCAmpPhase* signal);  /* Output: structure for the generated signal */
/* Function generating a LLV signal as a frequency series in Re/Im form where the modes have been summed, from LLV parameters - takes as argument the frequencies on which to evaluate */
int LLVGenerateSignalReIm(
  struct tagLLVParams* params,       /* Input: set of LLV parameters of the template */
  gsl_vector* freq,                   /* Input: frequencies on which evaluating the waveform (from the injection) */
  struct tagLLVSignalReIm* signal);  /* Output: structure for the generated signal */
/* Function generating a LLV injection as a frequency series in Re/Im form where the modes have been summed, from LLV parameters - frequencies on which to evaluate are to be determined internally */
int LLVGenerateInjectionReIm(
  struct tagLLVParams* injectedparams,       /* Input: set of LLV parameters of the injection */
  double fLow,                                /* Input: starting frequency (from argument minf) */
  double fHigh,                               /* Input: upper frequency (from argument maxf) */
  int nbpts,                                  /* Input: number of frequency samples */
  int tagsampling,                            /* Input: tag for using linear (0) or logarithmic (1) sampling */
  struct tagLLVInjectionReIm* signal);       /* Output: structure for the generated signal */

// checks prior boundaires
int PriorBoundaryCheck(LLVPrior *prior, double *Cube);

// Prior functions from Cube to physical parameters
// x1 is min, x2 is max when specified
// r is Cube value
double CubeToFlatPrior(double r, double x1, double x2);
double CubeToLogFlatPrior(double r, double x1, double x2);
double CubeToPowerPrior(double p, double r, double x1, double x2);
double CubeToGaussianPrior(double r, double mean, double sigma);
double CubeToSinPrior(double r, double x1, double x2);
double CubeToCosPrior(double r, double x1, double x2);

/* Prior functions from physical parameters to Cube
   x1 is min, x2 is max when specified
   y is physical value */
double FlatPriorToCube(double y, double x1, double x2);
double LogFlatPriorToCube(double y, double x1, double x2);
double PowerPriorToCube(double p, double y, double x1, double x2);
double SinPriorToCube(double y, double x1, double x2);
double CosPriorToCube(double y, double x1, double x2);

/* log-Likelihood functions */
double CalculateLogLCAmpPhase(LLVParams *params, LLVInjectionCAmpPhase* injection);
double CalculateLogLReIm(LLVParams *params, LLVInjectionReIm* injection);

/************ Global Parameters ************/

extern LLVParams* injectedparams;
extern LLVGlobalParams* globalparams;
extern LLVPrior* priorParams;
double logZdata;

#endif


#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <getopt.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_rng.h>
#include "../fewbody.h"

int calc_units(fb_hier_t hier, fb_units_t *units)
{
  /* mass unit is total system mass */
  units->m = hier.hier[hier.hi[1]+0].m + hier.hier[hier.hi[1]+1].m +	\
    hier.hier[hier.hi[1]+2].m + hier.hier[hier.hi[1]+3].m +		\
    hier.hier[hier.hi[1]+4].m;
  /* length unit is one AU */
  units->l = FB_CONST_AU;
  /* everything else is derived */
  units->E = FB_CONST_G * fb_sqr(units->m) / units->l;
  units->v = sqrt(units->E/units->m);
  units->t = units->l / units->v;
  return(0);
}

int main(int argc, char *argv[])
{
  int j;
  double t;
  fb_hier_t hier;
  fb_input_t input;
  fb_ret_t retval;
  fb_units_t units;
  char string1[FB_MAX_STRING_LENGTH], string2[FB_MAX_STRING_LENGTH];

  /* set input parameters */
  input.ks = 0; /* turn K-S regularization off */
  input.tstop = 1.0e4; /* stopping time in units of units.t */
  input.Dflag = 0; /* don't output dynamical info to stdout */
  input.dt = 0.0; /* irrelevant when Dflag=0 */
  input.tcpustop = 120.0; /* stopping CPU time in seconds */
  input.absacc = 1.0e-9; /* integrator absolute accuracy */
  input.relacc = 1.0e-9; /* integrator relative accuracy */
  input.ncount = 500; /* number of integration steps between calls to fb_classify() */
  input.tidaltol = 1.0e-6; /* tidal perturbation required to force numerical */
                           /* integration of a binary node */
  input.fexp = 1.0; /* radius expansion factor of merger products */
  fb_debug = 0;

  /* initialize a few things */
  t = 0.0;
  hier.nstarinit = 5;
  hier.nstar = 5;
  fb_malloc_hier(&hier);
  fb_init_hier(&hier);

  /* set stellar properties */
  for (j=0; j<hier.nstar; j++) {
    hier.hier[hier.hi[1]+j].ncoll = 1;
    hier.hier[hier.hi[1]+j].id[0] = j;
    snprintf(hier.hier[hier.hi[1]+j].idstring, FB_MAX_STRING_LENGTH, "%d", j);
    hier.hier[hier.hi[1]+j].n = 1;
    hier.hier[hier.hi[1]+j].obj[0] = NULL;
    hier.hier[hier.hi[1]+j].obj[1] = NULL;
    hier.hier[hier.hi[1]+j].Eint = 0.0;
    hier.hier[hier.hi[1]+j].Lint[0] = 0.0;
    hier.hier[hier.hi[1]+j].Lint[1] = 0.0;
    hier.hier[hier.hi[1]+j].Lint[2] = 0.0;
    hier.hier[hier.hi[1]+j].R = 1.0e-3 * FB_CONST_RSUN;
    hier.hier[hier.hi[1]+j].m = FB_CONST_MSUN;
    hier.hier[hier.hi[1]+j].x[2] = 0.0; /* everything in x-y plane */
    hier.hier[hier.hi[1]+j].v[2] = 0.0;
  }

  /* set some positions and velocities */
  hier.hier[hier.hi[1]+0].x[0] = 10.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+0].x[1] = 0.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+0].v[0] = 10.0e5;
  hier.hier[hier.hi[1]+0].v[1] = 0.0;

  hier.hier[hier.hi[1]+1].x[0] = 10.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+1].x[1] = 10.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+0].v[0] = -10.0e5;
  hier.hier[hier.hi[1]+0].v[1] = 0.0;

  hier.hier[hier.hi[1]+2].x[0] = -30.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+2].x[1] = -5.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+0].v[0] = 0.0;
  hier.hier[hier.hi[1]+0].v[1] = 5.0e5;

  hier.hier[hier.hi[1]+3].x[0] = 0.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+3].x[1] = 27.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+0].v[0] = -5.0e5;
  hier.hier[hier.hi[1]+0].v[1] = -4.0e5;

  hier.hier[hier.hi[1]+4].x[0] = 10.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+4].x[1] = -32.0 * FB_CONST_AU;
  hier.hier[hier.hi[1]+0].v[0] = 5.0e5;
  hier.hier[hier.hi[1]+0].v[1] = -1.0e5;

  /* set units with our own routine, then normalize */
  calc_units(hier, &units);
  fb_normalize(&hier, units);

  /* call Fewbody to evolve system */
  retval = fewbody(input, &hier, &t);

  /* all the rest is parsing the output */
  if (retval.retval == 1) {
    fprintf(stderr, "encounter complete.\n");
  } else {
    fprintf(stderr, "encounter NOT complete.\n");
  }

  fprintf(stderr, "final configuration:  %s  (%s)\n",
	  fb_sprint_hier(hier, string1),
	  fb_sprint_hier_hr(hier, string2));

  fprintf(stderr, "t_final=%.6g (%.6g yr)  t_cpu=%.6g s\n",	\
	  t, t*units.t/FB_CONST_YR, retval.tcpu);

  fprintf(stderr, "DeltaL/L0=%.6g  DeltaL=%.6g\n", retval.DeltaLfrac, retval.DeltaL);
  fprintf(stderr, "DeltaE/E0=%.6g  DeltaE=%.6g\n", retval.DeltaEfrac, retval.DeltaE);
  fprintf(stderr, "Rmin=%.6g (%.6g RSUN)  Rmin_i=%d  Rmin_j=%d\n",	\
	  retval.Rmin, retval.Rmin*units.l/FB_CONST_RSUN, retval.Rmin_i, retval.Rmin_j);
  fprintf(stderr, "Nosc=%d (%s)\n", retval.Nosc,
	  (retval.Nosc>=1?"resonance":"non-resonance"));

  fprintf(stderr, "orbital parameters of outermost binaries:\n");
  for (j=0; j<hier.nobj; j++) {
    if (hier.obj[j]->n >= 2) {
      fprintf(stderr, "j=%d  a=%.6g AU  e=%.6g\n", j,
	      hier.obj[j]->a * units.l / FB_CONST_AU, hier.obj[j]->e);
    }
  }

  fb_free_hier(hier);
  return(0);
}