/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef delaunay_3673850c_175d_4077_bef2_fc69e19e5cf8_h
#define delaunay_3673850c_175d_4077_bef2_fc69e19e5cf8_h

#include <gslib/std.h>
#include <gslib/math.h>
#include <ariel/config.h>

/*
 * A implementation of the Delaunay Triangulation and the Constrained Delaunay Triangulation.
 * The original implementation was:
 * https://code.google.com/archive/p/cgprojects09/
 * further more, modifications were done to achieve the constrained version, visit:
 * http://www.geom.uiuc.edu/~samuelp/del_project.html
 */

__ariel_begin__

class ariel_export dt_joint
{
protected:
    vec2            _point;
    void*           _binding;

public:
    dt_joint() { _binding = nullptr; }
    dt_joint(const vec2& p, void* b) { _point = p, _binding = b; }
    void set_point(const vec2& p) { _point = p; }
    const vec2& get_point() const { return _point; }
    void set_binding(void* b) { _binding = b; }
    void* get_binding() const { return _binding; }
};

class ariel_export dt_edge
{
protected:
    dt_joint*       _org;
    dt_edge*        _prev;
    dt_edge*        _next;
    dt_edge*        _symmetric;
    bool            _constraint;
    bool            _checked;

public:
    dt_edge();
    void set_org(dt_joint* j) { _org = j; }
    dt_joint* get_org() const { return _org; }
    void set_dest(dt_joint* j) { _symmetric->set_org(j); }
    dt_joint* get_dest() const { return _symmetric->get_org(); }
    void set_prev_edge(dt_edge* e) { _prev = e; }
    dt_edge* get_prev_edge() const { return _prev; }
    void set_next_edge(dt_edge* e) { _next = e; }
    dt_edge* get_next_edge() const { return _next; }
    void set_symmetric(dt_edge* e) { _symmetric = e; }
    dt_edge* get_symmetric() const { return _symmetric; }
    const vec2& get_org_point() const { return _org->get_point(); }
    const vec2& get_dest_point() const { return get_dest()->get_point(); }
    void* get_org_binding() const { return _org->get_binding(); }
    void* get_dest_binding() const { return get_dest()->get_binding(); }
    dt_edge* get_org_next() const { return _prev->get_symmetric(); }
    dt_edge* get_org_prev() const { return _symmetric->get_next_edge(); }
    dt_edge* get_dest_next() const { return _symmetric->get_prev_edge(); }
    dt_edge* get_dest_prev() const { return _next->get_symmetric(); }
    dt_edge* get_left_next() const { return _next; }
    dt_edge* get_left_prev() const { return _prev; }
    dt_edge* get_right_next() const { return _symmetric->get_next_edge()->get_symmetric(); }
    dt_edge* get_right_prev() const { return _symmetric->get_prev_edge()->get_symmetric(); }
    void set_constraint(bool b) { _constraint = b; }
    bool is_constraint() const { return _constraint; }
    void set_checked(bool c) { _checked = c; }
    bool is_checked() const { return _checked; }
    bool is_outside_boundary() const;
    bool is_boundary() const;
};

struct ariel_export dt_edge_range
{
    dt_edge*        left;
    dt_edge*        right;

    dt_edge_range()  { left = right = nullptr; }
};

typedef list<dt_joint> dt_input_joints;
typedef list<dt_joint*> dt_joint_ptrs;
typedef vector<dt_joint*> dt_joints;
typedef unordered_set<dt_edge*> dt_edges;
typedef vector<dt_edge*> dt_edge_list;

class ariel_export delaunay_triangulation
{
public:
    delaunay_triangulation() {}
    ~delaunay_triangulation() { clear(); }
    void initialize(dt_input_joints& inputs);
    void run();
    void clear();
    dt_edge* add_constraint(const vec2& p1, const vec2& p2);
    void trim(dt_edge_list& edges);
    void set_range_left(dt_edge* e) { _edge_range.left = e; }
    void tracing() const;
    void trace_heuristically() const;
    void trace_mel() const;

protected:
    dt_joints       _sorted_joints;
    dt_edge_range   _edge_range;
    dt_edges        _edge_holdings;

protected:
    bool is_in_range(int begin, int end, dt_joint* joint);
    dt_edge_range delaunay(int begin, int end);
    dt_edge* create_edge_pair();
    dt_edge* connect_edges(dt_edge* e1, dt_edge* e2);
    void destroy_edge_pair(dt_edge* e);
    void shrink_triangulate(dt_edge* cut);
    void shrink_recursively(dt_edge_list& strips, dt_edge_list& temps);
    void collect_trim_edges(dt_edge_list& edges, dt_edge* e);

    template<class _visit>
    static void traverse_triangles_recursively(dt_edge* e, _visit visit)
    {
        assert(e);
        if(e->is_checked())
            return;
        auto do_visit = [&visit](dt_edge* e) {
            assert(e);
            auto* e1 = e->get_prev_edge();
            auto* e2 = e->get_next_edge();
            assert(e1 && e2 && !e1->is_checked() && !e2->is_checked());
            void* b1 = e->get_org_binding();
            void* b2 = e->get_dest_binding();
            void* b3 = e1->get_org_binding();
            void* b4 = e2->get_dest_binding();
            if(b3 != b4)    /* invalid triangle */
                return;
            auto& p1 = e->get_org_point();
            auto& p2 = e->get_dest_point();
            auto& p3 = e1->get_org_point();
            if(!is_concave_angle(p1, p2, p3))     /* ccw! */
                return;
            bool b[3];
            b[0] = e->is_boundary();
            b[1] = e1->is_boundary();
            b[2] = e2->is_boundary();
            visit(b1, b2, b3, b);
            e->set_checked(true);
            e1->set_checked(true);
            e2->set_checked(true);
        };
        do_visit(e);
        traverse_triangles_recursively(e->get_symmetric(), visit);
        traverse_triangles_recursively(e->get_prev_edge()->get_symmetric(), visit);
        traverse_triangles_recursively(e->get_next_edge()->get_symmetric(), visit);
    }
    template<class _visit>
    static void traverse_triangles_flatly(dt_edge* e, _visit visit)
    {
        vector<dt_edge*> edges;
        edges.push_back(e);
        while(!edges.empty()) {
            e = edges.back();
            edges.pop_back();
            if(e->is_checked())
                continue;
            auto do_visit = [&visit](dt_edge* e) {
                assert(e);
                auto* e1 = e->get_prev_edge();
                auto* e2 = e->get_next_edge();
                assert(e1 && e2 && !e1->is_checked() && !e2->is_checked());
                void* b1 = e->get_org_binding();
                void* b2 = e->get_dest_binding();
                void* b3 = e1->get_org_binding();
                void* b4 = e2->get_dest_binding();
                if(b3 != b4)    /* invalid triangle */
                    return;
                auto& p1 = e->get_org_point();
                auto& p2 = e->get_dest_point();
                auto& p3 = e1->get_org_point();
                if(!is_concave_angle(p1, p2, p3))     /* ccw! */
                    return;
                bool b[3];
                b[0] = e->is_boundary();
                b[1] = e1->is_boundary();
                b[2] = e2->is_boundary();
                visit(b1, b2, b3, b);
                e->set_checked(true);
                e1->set_checked(true);
                e2->set_checked(true);
            };
            do_visit(e);
            edges.push_back(e->get_symmetric());
            edges.push_back(e->get_prev_edge()->get_symmetric());
            edges.push_back(e->get_next_edge()->get_symmetric());
        }
    }

public:
    template<class _visit>
    void traverse_triangles(_visit visit)
    {
        traverse_triangles_flatly(_edge_range.left, visit);
    }
    void reset_traverse();
};

__ariel_end__

#endif


#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <mpi.h>
#include "compearth.h"
#include "parmt_mtsearch.h"
#ifdef PARMT_USE_INTEL
#include <mkl_lapacke.h>
#include <mkl_cblas.h>
#else
#include <lapacke.h>
#include <cblas.h>
#endif
#include "iscl/array/array.h"
#include "iscl/memory/memory.h"

/*
int main_temp()
{
    double *betas, *gammas, *kappas, *M0s, *mts, *sigmas, *thetas;
    int i, ierr, nmt;
    const int ng = 29;
    const int nb = 29;
    const int nk = 6;
    const int ns = 6;
    const int nt = 6;
    const int nm = 1;
    const int ldm = 8;
    const double oneDeg = M_PI/180.0;
    const double betaMin = 0.0 + oneDeg;
    const double betaMax = M_PI - oneDeg;
    const double gammaMin =-M_PI/6.0 + oneDeg;
    const double gammaMax = M_PI/6.0 - oneDeg;
    const double kappaMin = 0.0 + oneDeg;
    const double kappaMax = 2.0*M_PI - oneDeg;
    const double thetaMin = 0.0 + oneDeg;
    const double thetaMax = 0.5*M_PI - oneDeg;
    const double sigmaMin =-0.5*M_PI + oneDeg;
    const double sigmaMax = 0.5*M_PI - oneDeg;
    nmt = nm*nb*ng*nk*ns*nt;
    mts = memory_calloc64f(nmt*ldm); //(double *)calloc((size_t) (nmt*ldm), sizeof(double));
    M0s = memory_calloc64f(nm); //(double *)calloc((size_t) nm, sizeof(double));
    betas = memory_calloc64f(nb); //(double *)calloc((size_t) nb, sizeof(double));
    gammas = memory_calloc64f(ng); //(double *)calloc((size_t) ng, sizeof(double));
    kappas = memory_calloc64f(nk); //(double *)calloc((size_t) nk, sizeof(double));
    sigmas = memory_calloc64f(ns); //(double *)calloc((size_t) ns, sizeof(double));
    thetas = memory_calloc64f(nt); //(double *)calloc((size_t) nt, sizeof(double));

    ierr = array_linspace64f_work(1.0/sqrt(2.0), 1.0/sqrt(2.0), nm, M0s);
    ierr = array_linspace64f_work(betaMin+0.001,  betaMax-0.001,  nb, betas);
    ierr = array_linspace64f_work(gammaMin, gammaMax, ng, gammas);
    ierr = array_linspace64f_work(kappaMin, kappaMax, nk, kappas);
    ierr = array_linspace64f_work(thetaMin, thetaMax, nt, thetas);
    ierr = array_linspace64f_work(sigmaMin, sigmaMax, ns, sigmas);
    // avoid warnings on theta in tt2cmt
    ierr = gridSearch_discretizeMT64f(ng, gammas, nb, betas,  nm, M0s,
                                      nk, kappas, nt, thetas, ns, sigmas,
                                      ldm, nmt, mts);

double Mdc[6], lam[3], U[6], theta;
    compearth_tt2cmt(0.0, 0.0, 1./sqrt(2.),
                     0.0, 0.1, 0.0, Mdc, lam, U);

    FILE *ofl = fopen("mts.txt", "w");
    for (i=0; i<nmt; i++)
    {
        compearth_angleMT(1, Mdc, &mts[ldm*i], &theta);

        fprintf(ofl, "%f %f %f %f %f %f %f\n", //i+1,
                mts[ldm*i+0], mts[ldm*i+1], mts[ldm*i+2],
                mts[ldm*i+3], mts[ldm*i+4], mts[ldm*i+5], theta);
    }
    fclose(ofl);

    free(mts);
    free(M0s);
    free(betas);
    free(gammas);
    free(kappas);
    free(sigmas);
    free(thetas);
    return 0;
}
*/
//============================================================================//
/*!
 * @brief Provides a cell based discretization of the moment tensor space.
 *
 * @param[in] nb          Number of colatitudes.
 * @param[in] betaLower   Lower colatitude in grid search [0, betaUpper).
 * @param[in] betaUpper   Upper colatitude in grid search (betaLower, pi].
 * @param[in] ng          Number of longitudes.
 * @param[in] gammaLower  Lower longitude in grid search [-pi/6, gammaUpper).
 * @param[in] gammaUpper  Upper longitude in grid search (gammaLower, pi/6].
 * @param[in] nk          Number of strikes in grid search.
 * @param[in] kappaLower  Lower strike in grid search [0, kappaUpper).
 * @param[in] kappaUpper  Upper strike in grid search (kappaLower, 2*pi].
 * @param[in] ns          Number of slips in grid search.
 * @param[in] sigmaLower  Lower slip angle in grid search [-pi, sigmaUpper).
 * @param[in] sigmaUpper  Upper slip angle in grid search (sigmaUpper, pi].
 * @param[in] nt          Number of dips in grid search.
 * @param[in] thetaLower  Lower dip in grid search [0,dipUpper).
 * @param[in] thetaUpper  Upper dip in grid search (dipLower, pi/2].
 * @param[in] nm          Number of magnitudes.
 * @param[in] m0Lower     Lower scalar moment in grid search (Newton-meters).
 * @param[in] m0Upper     Upper scalar moment in grid search (Newton-meters).
 * @param[in] luseLog     If true then the the scalar moments will be
 *                        discretized on a log scale.
 *
 * @param[out] betas      Colatitudes (radians) at cell centers [nb].
 * @param[out] gammas     Longitudes (radians) at cell centers [ng].
 * @param[out] kappas     Strike angles (radians) at cell centers [nk].
 * @param[out] sigmas     Slip angles (radians) at cell centers [ns].
 * @param[out] thetas     Dip angles (radians) at cell centers [nt].
 * @param[out] M0s        Scalar moments (Newton-meters) in grid search [nm].
 *
 * @author Ben Baker
 *
 * @copyright ISTI distributed under Apache 2
 *
 */
int parmt_discretizeCells64f(
    const int nb, const double betaLower, const double betaUpper,
    const int ng, const double gammaLower, const double gammaUpper,
    const int nk, const double kappaLower, const double kappaUpper,
    const int ns, const double sigmaLower, const double sigmaUpper,
    const int nt, const double thetaLower, const double thetaUpper,
    const int nm, const double m0Lower, const double m0Upper,
    const bool luseLog,
    double **betas,  double **gammas, double **kappas,
    double **sigmas, double **thetas, double **M0s)
{
    const char *fcnm = "parmt_discretizeCells64f\0";
    double *mwl, *u, *v, *h;
    double du, du2, dv, dv2, dh, dh2, dk, dk2, ds, ds2,
           hLower, hUpper, mwll, mwul, uLower, uUpper, vLower, vUpper;
    int ierr;
    const double two = 2.0;
    // Initialize and do some basic error checks
    u = NULL;
    v = NULL;
    h = NULL;
    if (nb < 1 || ng < 1 || nk < 1 || ns < 1 || nt < 1 || nm < 1)
    {
        if (nb < 1){printf("%s: no betas\n", fcnm);}
        if (ng < 1){printf("%s: no gammas\n", fcnm);}
        if (nk < 1){printf("%s: no kappas\n", fcnm);}
        if (ns < 1){printf("%s: no sigmas\n", fcnm);}
        if (nt < 1){printf("%s: no thetas\n", fcnm);}
        if (nm < 1){printf("%s: no magnitudes\n", fcnm);}
        return -1;
    }
    // Discretize the moment tensor space in (u, v, h) space
    compearth_beta2u(1, &betaLower, &uLower);
    compearth_beta2u(1, &betaUpper, &uUpper);
    compearth_gamma2v(1, &gammaLower, &vLower);
    compearth_gamma2v(1, &gammaUpper, &vUpper);
    compearth_theta2h(1, &thetaLower, &hLower);
    compearth_theta2h(1, &thetaUpper, &hUpper);
    du = (uUpper - uLower)/(double) nb;
    dv = (vUpper - vLower)/(double) ng;
    dh =-(hUpper - hLower)/(double) nt; // negative sign makes +theta increasing
    dk = (kappaUpper - kappaLower)/(double) nk;
    ds = (sigmaUpper - sigmaLower)/(double) ns;
    du2 = du/two;
    dv2 = dv/two;
    dh2 = dh/two;
    u = array_linspace64f(uLower+du2, uUpper-du2, nb, &ierr);
    v = array_linspace64f(vLower+dv2, vUpper-dv2, ng, &ierr);
    h = array_linspace64f(hLower-dh2, hUpper+dh2, nt, &ierr);
    *betas  = memory_calloc64f(nb);
    *gammas = memory_calloc64f(ng);
    *thetas = memory_calloc64f(nt);
    //compearth_u2beta(nb, 20, 2, u, 1.e-6, *betas);
    compearth_u2beta(nb, u, *betas);
    compearth_v2gamma(ng, v, *gammas);
    compearth_h2theta(nt, h, *thetas);
    // discretize the magnitudes, kappa, and sigma
    dk2 = dk/two;
    ds2 = ds/two;
    if (!luseLog || nm == 1)
    {
        *M0s = array_linspace64f(m0Lower, m0Upper, nm, &ierr);
    }
    else
    {
        // TODO: I doubt this is correct because I don't understand how
        // lune volumes map to `spherical volumes'.  For simplicity I'll
        // use a shell but I think I should be looking at the `active
        // area' of the beta and gamma grid search.  In the interim I'll
        // use the volume of a spherical shell which is given by
        // \frac{4 \pi R^3}{3} where R is the radius (magnitude).
        // To get some form of uniform volume spacing I'll take the log
        // at the upper and lower limit and discretize evenly in logspace
        // then come back to M0.
        /*
        m0ll = 3.0*log(4.0*M_PI*m0Lower/3.0);
        m0ul = 3.0*log(4.0*M_PI*m0Upper/3.0);
        m0l = array_linspace64f(m0ll, m0ul, nm, &ierr);
        // Now solve for m0: 3/(4*pi)*exp(m0l/3)
        cblas_dscal(nm, 1.0/3.0, m0l, 1);         // m0l = m0l/3
        *M0s = array_exp64f(nm, m0l, &ierr);      // *M0 = exp(m0l/3)
        cblas_dscal(nm, 3.0/(4.0*M_PI), *M0s, 1); // *M0 = 3/(4*pi)exp(m0l/3)
        memory_free64f(&m0l);
        */
        // TODO: An alternative to doing math is just discretizing on a
        // the magnitude scale
        compearth_m02mw(1, CE_KANAMORI_1978, &m0Lower, &mwll);
        compearth_m02mw(1, CE_KANAMORI_1978, &m0Upper, &mwul);
        mwl = array_linspace64f(mwll, mwul, nm, &ierr);
        *M0s = memory_calloc64f(nm);
        ierr = compearth_mw2m0(nm, CE_KANAMORI_1978, mwl, *M0s);
        memory_free64f(&mwl);
    }
    *kappas = array_linspace64f(kappaLower+dk2, kappaUpper-dk2, nk, &ierr);
    *sigmas = array_linspace64f(sigmaLower+ds2, sigmaUpper-ds2, ns, &ierr);
    memory_free64f(&u);
    memory_free64f(&v);
    memory_free64f(&h);
    return ierr;
}

//============================================================================//
/*!
 * @brief Computes the moment tensors in the grid search.
 *        The grid search ordering is:
 *         Loop on magnitudes
 *          Loop on colatitudes
 *           Loop on longitudes
 *            Loop on strikes
 *             Loop on slips
 *              Loop on dips
 *
 * @param[in] comm    MPI communicator on which moment tensor will be split
 * @param[in] ng      number of longitudes
 * @param[in] gammas  longitudes (radians) on lune s.t.
 *                    \f$ \gamma \in [-\pi/6, \pi/6] \f$ [ng]
 * @param[in] nb      number of colatitudes
 * @param[in] betas   colatitudes (radians) on lune s.t.
 *                    \f$ \beta \in [0, \pi] \f$ [nb]
 * @param[in] nm      number of scalar moments
 * @param[in] M0s     scalar moments in Newton-meters [nm].
 *                    for a `unit' moment tensor choose M0 = 1/sqrt(2)
 * @param[in] nk      number of strike angles
 * @param[in] kappas  strike angles (radians) s.t.
 *                    \f$ \kappa \in [0, 2\pi] \f$ [nk]
 * @param[in] nt      number of dip angles
 * @param[in] thetas  dips angles (radians) s.t.
 *                    \f$ \theta \in [0, \pi/2] \f$ [nt]
 * @param[in] ns      number of slip angles
 * @param[in] sigmas  slip angles (radians) s.t.
 *                    \f$ \sigma \in [-\pi/2, \pi/2] \f$ [ns]
 * @param[in] ldm     leading dimension of mts.  must be >= 6.
 * @param[in] nmt     number of moment tensors (=nm*nb*ng*nk*ns*nt)
 *
 * @param[out] mts    moment tensors (Newton-meters) in north,east,down s.t.
 *                    \f$ \textbf{m}
 *                      = \f$ \{m_{xx}, m_{yy}, m_{zz},
 *                              m_{xy}, m_{xz}, m_{yz}\} \f$.
 *                    This has dimensions [nm*nb*ng*nk*ns*nt x ldm].  Observe
 *                    the ordering as defind above.
 *
 * @result 0 indicates success
 *
 * @author Ben Baker
 *
 * @copyright ISTI licensed under Apached 2
 *
 */
int parmt_discretizeMT64f_MPI(const MPI_Comm comm,
                              const int ng,
                              const double *__restrict__ gammas,
                              const int nb,
                              const double *__restrict__ betas,
                              const int nm,
                              const double *__restrict__ M0s,
                              const int nk,
                              const double *__restrict__ kappas,
                              const int nt,
                              const double *__restrict__ thetas,
                              const int ns,
                              const double *__restrict__ sigmas,
                              const int ldm,
                              struct localMT_struct *mts)
{
    const char *fcnm = "parmt_discretizeMT64f_MPI\0";
    double *mtWork;
    int64_t nmt64;
    int *displ, *nmtProc, *offset, *sendCounts;
    int dmt, i, ierr, imt1, imt2, myid, nmtall, nmt, nprocs;
    const int master = 0;
    //------------------------------------------------------------------------//
    ierr = 0;
    MPI_Comm_size(comm, &nprocs);
    MPI_Comm_rank(comm, &myid);
    memset(mts, 0, sizeof(struct localMT_struct));
    mts->comm = comm;
    // Check for integer overflow
    nmt64 = (int64_t) ng * (int64_t) nb * (int64_t) nm
           *(int64_t) nk * (int64_t) nt * (int64_t) ns;
    if (nmt64 > INT_MAX)
    {
        printf("%s: Error integer overflow - gridsearch too large\n", fcnm);
        return -1;
    }
    nmt = (int) nmt64;
    // Verify the inputs
    if (ldm < 6 || ng < 1 || nb < 1 || nm < 1 || nk < 1 || nt < 1 || ns < 1 ||
        gammas == NULL || betas == NULL || M0s == NULL || kappas == NULL ||
        thetas == NULL || sigmas == NULL)
    {
        if (ldm < 6){printf("%s: Invalid leading dimension\n", fcnm);}
        if (ng < 1){printf("%s: ng must be positive\n", fcnm);}
        if (nb < 1){printf("%s: nb must be positive\n", fcnm);}
        if (nm < 1){printf("%s: nm must be positive\n", fcnm);}
        if (nk < 1){printf("%s: nk must be positive\n", fcnm);}
        if (nt < 1){printf("%s: nt must be positive\n", fcnm);}
        if (ns < 1){printf("%s: ns must be positive\n", fcnm);}
        if (gammas == NULL){printf("%s: gammas is NULL\n", fcnm);}
        if (betas == NULL){printf("%s: betas is NULL\n", fcnm);}
        if (M0s == NULL){printf("%s: M0s is NULL\n", fcnm);}
        if (kappas == NULL){printf("%s: kappas is NULL\n", fcnm);}
        if (thetas == NULL){printf("%s: thetas is NULL\n", fcnm);}
        if (sigmas == NULL){printf("%s: sigmas is NULL\n", fcnm);}
        return -1;
    }
    // divide the moment tensor grid
    dmt = MAX(nmt/nprocs, 1);
    imt1 = myid*dmt;
    imt2 = (myid + 1)*dmt;
    if (myid == nprocs - 1){imt2 = nmt;}
    mts->nmt = imt2 - imt1;
    mts->ldm = ldm;
    MPI_Allreduce(&mts->nmt, &nmtall, 1, MPI_INTEGER, MPI_SUM, mts->comm);
    if (nmtall != nmt)
    {
        if (myid == master)
        {
            printf("%s: Failed to partition domain %d %d\n", fcnm, nmtall, nmt);
        }
        return -1;
    }
    // create the requisite information for an MPI_GATHER
    mts->nmtProc = memory_calloc32i(nprocs);
    mts->offset = memory_calloc32i(nprocs);
    nmtProc = memory_calloc32i(nprocs);
    offset = memory_calloc32i(nprocs);
    nmtProc[myid] = mts->nmt;
    offset[myid] = imt1;
    MPI_Allreduce(nmtProc, mts->nmtProc, nprocs, MPI_INTEGER,
                  MPI_SUM, mts->comm);
    MPI_Allreduce(offset, mts->offset,  nprocs, MPI_INTEGER,
                  MPI_SUM, mts->comm);
    mts->commSize = nprocs;
    mts->nmtAll = nmtall;
    mts->myid = myid;
    memory_free32i(&nmtProc);
    memory_free32i(&offset);
    // have master process compute all mts and scatter them
    // TODO this should be parallel
    if (mts->myid == master)
    {
        mtWork = memory_calloc64f(mts->ldm*mts->nmtAll);
        ierr = parmt_discretizeMT64f(ng, gammas,
                                     nb, betas,
                                     nm, M0s,
                                     nk, kappas,
                                     nt, thetas,
                                     ns, sigmas,
                                     mts->ldm, mts->nmtAll, mtWork);
    }
    else
    {
        mtWork = memory_calloc64f(1);
    }
    MPI_Bcast(&ierr, 1, MPI_INT, master, mts->comm);
    if (mts->commSize > 1)
    {
        // figure out who gets what part of the moment tensor space
        sendCounts = memory_calloc32i(mts->commSize);
        displ = memory_calloc32i(mts->commSize);
        for (i=0; i<mts->commSize; i++)
        {
            sendCounts[i] = mts->nmtProc[i]*mts->ldm;
            displ[i] = mts->ldm*mts->offset[i];
        }
        // set the memory and distribute it
        mts->mts = memory_calloc64f(mts->ldm*MAX(1,mts->nmt));
        MPI_Scatterv(mtWork, sendCounts, displ,
                     MPI_DOUBLE, mts->mts, mts->ldm*mts->nmt,
                     MPI_DOUBLE, master, mts->comm);
        memory_free64f(&mtWork);
    }
    else
    {
        mts->mts = mtWork;
    }
    return ierr;
}
//============================================================================//
/*!
 * @brief Computes the moment tensors in the grid search.
 *        The grid search ordering is:
 *         Loop on magnitudes
 *          Loop on colatitudes
 *           Loop on longitudes
 *            Loop on strikes
 *             Loop on slips
 *              Loop on dips
 *
 * @param[in] ng      number of longitudes
 * @param[in] gammas  longitudes (radians) on lune s.t.
 *                    \f$ \gamma \in [-\pi/6, \pi/6] \f$ [ng]
 * @param[in] nb      number of colatitudes
 * @param[in] betas   colatitudes (radians) on lune s.t.
 *                    \f$ \beta \in [0, \pi] \f$ [nb]
 * @param[in] nm      number of scalar moments
 * @param[in] M0s     scalar moments in Newton-meters [nm].
 *                    for a `unit' moment tensor choose M0 = 1/sqrt(2)
 * @param[in] nk      number of strike angles
 * @param[in] kappas  strike angles (radians) s.t.
 *                    \f$ \kappa \in [0, 2\pi] \f$ [nk]
 * @param[in] nt      number of dip angles
 * @param[in] thetas  dips angles (radians) s.t.
 *                    \f$ \theta \in [0, \pi/2] \f$ [nt]
 * @param[in] ns      number of slip angles
 * @param[in] sigmas  slip angles (radians) s.t.
 *                    \f$ \sigma \in [-\pi/2, \pi/2] \f$ [ns]
 * @param[in] ldm     leading dimension of mts.  must be >= 6.
 * @param[in] nmt     number of moment tensors (=nm*nb*ng*nk*ns*nt)
 *
 * @param[out] mts    moment tensors (Newton-meters) in north,east,down s.t.
 *                    \f$ \textbf{m}
 *                      = \f$ \{m_{xx}, m_{yy}, m_{zz},
 *                              m_{xy}, m_{xz}, m_{yz}\} \f$.
 *                    This has dimensions [nm*nb*ng*nk*ns*nt x ldm].  Observe
 *                    the ordering as defind above.
 *
 * @result 0 indicates success
 *
 * @author Ben Baker
 *
 * @copyright ISTI licensed under Apached 2
 *
 */
int parmt_discretizeMT64f(const int ng,
                          const double *__restrict__ gammas,
                          const int nb,
                          const double *__restrict__ betas,
                          const int nm,
                          const double *__restrict__ M0s,
                          const int nk,
                          const double *__restrict__ kappas,
                          const int nt,
                          const double *__restrict__ thetas,
                          const int ns,
                          const double *__restrict__ sigmas,
                          const int ldm,
                          const int nmt, double *__restrict__ mts)
{
    const char *fcnm = "parmt_discretizeMT64f\0";
    double lam[3], Muse[6], U[9] __attribute__ ((aligned (64)));
    double *mtWork, deltaDeg, gammaDeg, kappaDeg, sigmaDeg, thetaDeg;
    int i, ierr, ierr1, ib, ig, indx, im, imt, ik, is, it, nmtBase;
    const double M01[1] = {1.0};
    const double pi180i = 180.0/M_PI;
    const double betaMin = 0.0;
    const double betaMax = M_PI;
    const double gammaMin =-M_PI/6.0;
    const double gammaMax = M_PI/6.0;
    const double kappaMin = 0.0;
    const double kappaMax = 2.0*M_PI;
    const double thetaMin = 0.0;
    const double thetaMax = M_PI_2;
    const double sigmaMin =-M_PI_2;
    const double sigmaMax = M_PI_2;
    //const double sqrt2i = 1.0/sqrt(2.0); // unit magnitude MT
    //------------------------------------------------------------------------//
    ierr = 0;
    // Verify the inputs
    if (ldm < 6 || ng < 1 || nb < 1 || nm < 1 || nk < 1 || nt < 1 || ns < 1 ||
        nmt != ng*nb*nm*nk*nt*ns ||
        gammas == NULL || betas == NULL || M0s == NULL || kappas == NULL ||
        thetas == NULL || sigmas == NULL)
    {
        if (ldm < 6){printf("%s: Invalid leading dimension\n", fcnm);}
        if (ng < 1){printf("%s: ng must be positive\n", fcnm);}
        if (nb < 1){printf("%s: nb must be positive\n", fcnm);}
        if (nm < 1){printf("%s: nm must be positive\n", fcnm);}
        if (nk < 1){printf("%s: nk must be positive\n", fcnm);}
        if (nt < 1){printf("%s: nt must be positive\n", fcnm);}
        if (ns < 1){printf("%s: ns must be positive\n", fcnm);}
        if (nmt != ng*nb*nm*nk*nt*ns)
        {
            printf("%s: nmt != ng*nb*nm*nk*nt*ns %d %d\n",
                   fcnm, nmt, ng*nb*nm*nk*nt*ns);
        }
        if (gammas == NULL){printf("%s: gammas is NULL\n", fcnm);}
        if (betas == NULL){printf("%s: betas is NULL\n", fcnm);}
        if (M0s == NULL){printf("%s: M0s is NULL\n", fcnm);}
        if (kappas == NULL){printf("%s: kappas is NULL\n", fcnm);}
        if (thetas == NULL){printf("%s: thetas is NULL\n", fcnm);}
        if (sigmas == NULL){printf("%s: sigmas is NULL\n", fcnm);}
        return -1;
    }
    for (i=0; i<ng; i++)
    {
        if (gammas[i] < gammaMin || gammas[i] > gammaMax)
        {
            printf("%s: gammas %f out or range [%f,%f]\n",
                   fcnm, gammas[i], gammaMin, gammaMax);
            return -1;
        }
    }
    for (i=0; i<nb; i++)
    {
        if (betas[i] < betaMin || betas[i] > betaMax)
        {
            printf("%s: betas %f out or range [%f,%f]\n",
                   fcnm, betas[i], betaMin, betaMax);
            return -1;
        }
    }
    for (i=0; i<nk; i++)
    {
        if (kappas[i] < kappaMin || kappas[i] > kappaMax)
        {
            printf("%s: kappas %f out or range [%f,%f]\n",
                   fcnm, kappas[i], kappaMin, kappaMax);
            return -1;
        }
    }
    for (i=0; i<nt; i++)
    {
        if (thetas[i] < thetaMin || thetas[i] > thetaMax)
        {
            printf("%s: thetas %f out or range [%f,%f]\n",
                   fcnm, thetas[i], thetaMin, thetaMax);
            return -1;
        }
    }
    for (i=0; i<ns; i++)
    {
        if (sigmas[i] < sigmaMin || sigmas[i] > sigmaMax)
        {
            printf("%s: sigmas %f out or range [%f,%f]\n",
                   fcnm, sigmas[i], sigmaMin, sigmaMax);
            return -1;
        }
    }
    // The moment tensors are equivalent up to a scaling factor - so compute
    // the unit moment tensor
    nmtBase = nb*ng*nk*ns*nt;
    if (nm == 1)
    {
        mtWork = mts;
    }
    else
    {
        mtWork = memory_calloc64f(nmtBase*ldm);
    }
#ifdef _OPENMP
    #pragma omp parallel for collapse(5) \
     firstprivate (lam, M01, Muse, U) \
     private (deltaDeg, gammaDeg, kappaDeg, \
              sigmaDeg, thetaDeg, ierr1, imt, ib, ig, ik, is, it) \
     shared (fcnm, M0s, betas, gammas, kappas, sigmas, mtWork, thetas) \
     reduction (max:ierr) \
     default (none)
#endif
    // Loop on colatitudes
    for (ib=0; ib<nb; ib++)
    {
        // Loop on longitudes
        for (ig=0; ig<ng; ig++)
        {
            // Loop on strike
            for (ik=0; ik<nk; ik++)
            {
                // Loop on slip
                for (is=0; is<ns; is++)
                {
                    // Loop on dip
                    for (it=0; it<nt; it++)
                    {
                        // tape**2 space term
                        //M0 = sqrt2i;
                        deltaDeg = (M_PI/2.0 - betas[ib])*pi180i;
                        gammaDeg = gammas[ig]*pi180i;
                        kappaDeg = kappas[ik]*pi180i;
                        sigmaDeg = sigmas[is]*pi180i;
                        thetaDeg = thetas[it]*pi180i;
                        // index
                        imt = ib*ng*nk*ns*nt
                            + ig*nk*ns*nt
                            + ik*ns*nt
                            + is*nt
                            + it;
                        // Compute the corresponding moment tensor.
                        // Silver and Jordan compute M0 = 1/sqrt(2)norm(M)
                        // hence, if it is the scalar moment computation
                        // that handles the 1/sqrt(2) which means I should
                        // use unity here.  This has been verified b/c
                        // apply CMT2m0 on this moment tensor will yield
                        // the input M0.
                        ierr1 = compearth_TT2CMT(1, &gammaDeg, &deltaDeg, M01,
                                                 &kappaDeg, &thetaDeg,
                                                 &sigmaDeg,
                                                 Muse, lam, U);
/*
                        ierr1 = compearth_tt2cmt(gammaDeg, deltaDeg, 1.0, //sqrt2i,
                                                 kappaDeg, thetaDeg,
                                                 sigmaDeg,
                                                 Muse, lam, U);
*/
                        // Convert from USE to our NED estimation basis
                        ierr1 = compearth_convertMT(1, CE_USE, CE_NED, Muse,
                                                    &mtWork[imt*ldm]);
                        if (ierr1 != 0)
                        {
                            printf("%s: Error changing coords\n", fcnm);
                            ierr = ierr + 1;
                            continue;
                        }
                    } // loop on dip
                } // loop on slip
            } // loop on strike
        } // loop on longitudes
    } // loop on latitudes
    if (ierr != 0)
    {
        printf("%s: Errors during mt computation\n", fcnm);
        ierr = 1;
    }
    // Now copy all moment tensors over
    if (nm == 1)
    {
        cblas_dscal(ldm*nmtBase, M0s[0], mtWork, 1);
    }
    else
    {
        for (im=0; im<nm; im++)
        {
            indx = im*nmtBase*ldm; //nb*ng*nk*ns*nt;
            for (imt=0; imt<ldm*nmtBase; imt++)
            {
                mts[indx+imt] = M0s[im]*mtWork[imt];
            }
        }
        memory_free64f(&mtWork);
    }
    mtWork = NULL;
    return ierr;
}


/*
 * BRAINS
 * (B)LR (R)everberation-mapping (A)nalysis (I)n AGNs with (N)ested (S)ampling
 * Yan-Rong Li, liyanrong@ihep.ac.cn
 * Thu, Aug 4, 2016
 */

/*! \file dnest_line1d.c
 *  \brief run dnest sampling for 1d line analysis.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_interp.h>
#include <mpi.h>

#include "brains.h"

DNestFptrSet *fptrset_line1d;

/*!
 * This function setup functions for dnest and run dnest.
 */
int dnest_line1d(int argc, char **argv)
{
  int i;

  set_blr_model1d();

  num_params_blr = num_params_blr_model + 1; /* include line sys err */
  num_params_blr_tot = num_params_blr;
  num_params = parset.n_con_recon + num_params_var + num_params_blr_tot;
  idx_resp = num_params_blr_tot + num_params_drw + num_params_trend;
  idx_difftrend = idx_resp + num_params_resp;

  par_fix = (int *) malloc(num_params * sizeof(int));
  par_fix_val = (double *) malloc(num_params * sizeof(double));

  par_range_model = malloc( num_params * sizeof(double *));
  par_prior_gaussian = malloc(num_params * sizeof(double *));
  for(i=0; i<num_params; i++)
  {
    par_range_model[i] = malloc(2*sizeof(double));
    par_prior_gaussian[i] = malloc(2*sizeof(double));
  }
  par_prior_model = malloc( num_params * sizeof(int));

  fptrset_line1d = dnest_malloc_fptrset();

  /* setup functions used for dnest */
  fptrset_line1d->from_prior = from_prior_line1d;
  fptrset_line1d->print_particle = print_particle_line1d;
  fptrset_line1d->restart_action = restart_action_1d;
  fptrset_line1d->accept_action = accept_action_1d;
  fptrset_line1d->kill_action = kill_action_1d;
  fptrset_line1d->read_particle = read_particle_line1d;
  fptrset_line1d->perturb = perturb_line1d;

  if(parset.flag_exam_prior != 1)
  {
    fptrset_line1d->log_likelihoods_cal_initial = log_likelihoods_cal_initial_line1d;
    fptrset_line1d->log_likelihoods_cal_restart = log_likelihoods_cal_restart_line1d;
    fptrset_line1d->log_likelihoods_cal = log_likelihoods_cal_line1d;
  }
  else
  {
    fptrset_line1d->log_likelihoods_cal_initial = log_likelihoods_cal_line1d_exam;
    fptrset_line1d->log_likelihoods_cal_restart = log_likelihoods_cal_line1d_exam;
    fptrset_line1d->log_likelihoods_cal = log_likelihoods_cal_line1d_exam;
  }

  set_par_range_model1d();
  set_par_fix_blrmodel();

  for(i=num_params_blr_model; i<num_params; i++)
  {
    par_fix[i] = 0;
    par_fix_val[i] = -DBL_MAX;
  }

  /* fix continuum variation parameter sigma and tau if flag_fixvar is true */
  if(parset.flag_fixvar == 1)
  {
    par_fix[num_params_blr + 1] = 1;
    par_fix_val[num_params_blr + 1] = var_param[1];
    par_fix[num_params_blr + 2] = 1;
    par_fix_val[num_params_blr + 2] = var_param[2];
  }

  /* fix systematic error of line */
  if(parset.flag_line_sys_err != 1)
  {
    par_fix[num_params_blr-1] = 1;
    par_fix_val[num_params_blr-1] = log(1.0);
  }

  /* fix systematic error of continuum */
  if(parset.flag_con_sys_err != 1)
  {
    par_fix[num_params_blr] = 1;
    par_fix_val[num_params_blr] = log(1.0);
  }

  /* fix non-linear response */
  if(parset.flag_nonlinear !=1)
  {
    par_fix[idx_resp + 1] = 1;
    par_fix_val[idx_resp + 1] = 0.0;
  }

  print_par_names_model1d();

  force_update = parset.flag_force_update;
  if(parset.flag_para_name != 1)
    logz_line = dnest(argc, argv, fptrset_line1d, num_params, dnest_options_file);

  dnest_free_fptrset(fptrset_line1d);
  return 0;
}

/*!
 * this function setups parameter ranges.
 *
 * The order of parameters is:                              \n
 *   I.   blr model.............()                          \n
 *   II.  systematic error......()                          \n
 *   III. variability...........()                          \n
 *   IV.  long-term trend.......()                          \n
 *   V.   response A and Ag.....()                          \n
 *   VI.  different trend.......(if flag_difftend is ture)  \n
 *   VII. continuum light curve.()                          \n
 */
void set_par_range_model1d()
{
  int i, j, i1, i2;

  /* BLR parameters first */
  for(i=0; i<num_params_blr_model; i++)
  {
    par_range_model[i][0] = blr_range_model[i][0];
    par_range_model[i][1] = blr_range_model[i][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;
  }

  /* note that the last BLR parameters is the systematic error (1d) */
  i = num_params_blr -1;
  par_range_model[i][0] = sys_err_line_range[0];
  par_range_model[i][1] = sys_err_line_range[1];

  par_prior_model[i] = UNIFORM;
  par_prior_gaussian[i][0] = 0.0;
  par_prior_gaussian[i][1] = 0.0;

  /* variability parameters */
  /* first systematic error */
  i = num_params_blr;
  par_range_model[i][0] = var_range_model[0][0];
  par_range_model[i][1] = var_range_model[0][1];

  par_prior_model[i] = UNIFORM;
  par_prior_gaussian[i][0] = 0.0;
  par_prior_gaussian[i][1] = 0.0;

  for(i=num_params_blr+1; i<num_params_drw + num_params_blr; i++)
  {
    if(var_param_std[i-num_params_blr] > 0.0)
    {
      par_range_model[i][0] = var_param[i-num_params_blr] - 5.0 * var_param_std[i-num_params_blr];
      par_range_model[i][1] = var_param[i-num_params_blr] + 5.0 * var_param_std[i-num_params_blr];

      /* make sure that the range lies within the initial range */
      par_range_model[i][0] = fmax(par_range_model[i][0], var_range_model[i-num_params_blr][0]);
      par_range_model[i][1] = fmin(par_range_model[i][1], var_range_model[i-num_params_blr][1]);

      par_prior_model[i] = GAUSSIAN;
      par_prior_gaussian[i][0] = var_param[i-num_params_blr];
      par_prior_gaussian[i][1] = var_param_std[i-num_params_blr];
    }
    else
    {
      par_range_model[i][0] = var_range_model[i-num_params_blr][0];
      par_range_model[i][1] = var_range_model[i-num_params_blr][1];

      par_prior_model[i] = UNIFORM;
      par_prior_gaussian[i][0] = 0.0;
      par_prior_gaussian[i][1] = 0.0;
    }
  }
  /* long-term trend */
  for(i=num_params_drw + num_params_blr; i< num_params_drw + num_params_trend + num_params_blr; i++)
  {
    par_range_model[i][0] = var_range_model[3][0];
    par_range_model[i][1] = var_range_model[3][1];

    par_prior_model[i] = GAUSSIAN;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0;
  }
  /* response A and Ag */
  j = 0;
  i1 = idx_resp;
  i2 = idx_resp + num_params_resp;
  for(i=i1; i<i2; i++)
  {
    par_range_model[i][0] = resp_range[j][0];
    par_range_model[i][1] = resp_range[j][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;

    j++;
  }
  /* different trend */
  j = 0;
  i1 = idx_difftrend;
  i2 = idx_difftrend + num_params_difftrend;
  for(i=i1; i<i2; i++)
  {
    par_range_model[i][0] = var_range_model[4 + j][0];
    par_range_model[i][1] = var_range_model[4 + j][1];

    par_prior_model[i] = UNIFORM;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 0.0;

    j++;
  }

  /* continuum light curve parameters */
  for(i=num_params_blr+num_params_var; i<num_params; i++)
  {
    par_range_model[i][0] = var_range_model[4+num_params_difftrend][0];
    par_range_model[i][1] = var_range_model[4+num_params_difftrend][1];

    par_prior_model[i] = GAUSSIAN;
    par_prior_gaussian[i][0] = 0.0;
    par_prior_gaussian[i][1] = 1.0;
  }
  return;
}

/*!
 *  print names and prior ranges for parameters
 *
 */
void print_par_names_model1d()
{
  if(thistask!= roottask)
    return;

  int i, j;
  FILE *fp;
  char fname[BRAINS_MAX_STR_LENGTH], str_fmt[BRAINS_MAX_STR_LENGTH];

  sprintf(fname, "%s/%s", parset.file_dir, "data/para_names_model1d.txt");
  fp = fopen(fname, "w");
  if(fp == NULL)
  {
    fprintf(stderr, "# Error: Cannot open file %s.\n", fname);
    exit(0);
  }

  strcpy(str_fmt, "%4d %-15s %10.6f %10.6f %4d %4d %15.6e\n");

  printf("# Print parameter name in %s\n", fname);

  fprintf(fp, "#*************************************************\n");
  fprint_version(fp);
  fprintf(fp, "#*************************************************\n");

  fprintf(fp, "%4s %-15s %10s %10s %4s %4s %15s\n", "#", "Par", "Min", "Max", "Prior", "Fix", "Val");

  i=-1;
  for(j=0; j<num_params_blr_model; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "BLR model", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  i++;
  fprintf(fp, str_fmt, i, "sys_err_line", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  i++;
  fprintf(fp, str_fmt, i, "sys_err_con", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  i++;
  fprintf(fp, str_fmt, i, "sigmad", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  i++;
  fprintf(fp, str_fmt, i, "taud", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  for(j=0; j<num_params_trend; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "trend", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  i++;
  fprintf(fp, str_fmt, i, "A", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  i++;
  fprintf(fp, str_fmt, i, "Ag", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);

  for(j=0; j<num_params_difftrend; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "diff trend", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  for(j=0; j<parset.n_con_recon; j++)
  {
    i++;
    fprintf(fp, str_fmt, i, "time series", par_range_model[i][0], par_range_model[i][1], par_prior_model[i],
                            par_fix[i], par_fix_val[i]);
  }

  fclose(fp);
}

/*!
 * This function generates a sample from the prior.
 */
void from_prior_line1d(void *model)
{
  int i;
  double *pm = (double *)model;

  for(i=0; i<num_params; i++)
  {
    if(par_prior_model[i] == GAUSSIAN)
    {
      pm[i] = dnest_randn()*par_prior_gaussian[i][1] + par_prior_gaussian[i][0];
      dnest_wrap(&pm[i], par_range_model[i][0], par_range_model[i][1]);
    }
    else
    {
      pm[i] = par_range_model[i][0] + dnest_rand() * (par_range_model[i][1] - par_range_model[i][0]);
    }
  }
  /* cope with fixed parameters. */
  for(i=0; i<num_params_blr + num_params_var; i++)
  {
    if(par_fix[i] == 1)
      pm[i] = par_fix_val[i];
  }

  /* all parameters need to update at the initial step */
  which_parameter_update = -1;
  return;
}

/*!
 * This function calculate log likelihood probability at the initial step.
 */
double log_likelihoods_cal_initial_line1d(const void *model)
{
  double logL;
  logL = prob_initial_line1d(model);
  return logL;
}

/*!
 * This function calculate log likelihood probability at the initial step.
 */
double log_likelihoods_cal_restart_line1d(const void *model)
{
  double logL;
  logL = prob_restart_line1d(model);
  return logL;
}

/*!
 * This function calculate log likelihood probability.
 */
double log_likelihoods_cal_line1d(const void *model)
{
  double logL;
  logL = prob_line1d(model);
  return logL;
}

/*!
 * This function print out the particle into the file.
 */
void print_particle_line1d(FILE *fp, const void *model)
{
  int i;
  double *pm = (double *)model;

  for(i=0; i<num_params; i++)
  {
    fprintf(fp, "%e ", pm[i] );
  }
  fprintf(fp, "\n");
  return;
}

/*!
 * This function read the particle from the file.
 */
void read_particle_line1d(FILE *fp, void *model)
{
  int j;
  double *psample = (double *)model;

  for(j=0; j < dnest_num_params; j++)
  {
    if(fscanf(fp, "%lf", psample+j) < 1)
    {
      printf("%f\n", *psample);
      fprintf(stderr, "#Error: Cannot read file %s.\n", options.sample_file);
      exit(0);
    }
  }
  return;
}

/*!
 * this function perturbs the parameters.
 */
double perturb_line1d(void *model)
{
  double *pm = (double *)model;
  double logH = 0.0, limit1, limit2, width, rnd;
  int which, which_level;

  /*
   * sample BLR and variability parameters more frequently;
   * fixed parameter needs not to update.
   */
  do
  {
    rnd = dnest_rand();
    if(rnd < fmax(0.2, 1.0*(num_params_blr+num_params_var)/num_params))
      which = dnest_rand_int(num_params_blr + num_params_var);
    else
      which = dnest_rand_int(parset.n_con_recon) + num_params_blr + num_params_var;

  }while(par_fix[which]==1);

  which_parameter_update = which;

  /* level-dependent width */
  which_level_update = dnest_get_which_level_update();
  which_level = which_level_update > (size_levels - 10)?(size_levels -10):which_level_update;

  if( which_level > 0)
  {
    limit1 = limits[(which_level-1) * num_params *2 + which *2];
    limit2 = limits[(which_level-1) * num_params *2 + which *2 + 1];
    width = limit2 - limit1;
  }
  else
  {
    width = ( par_range_model[which][1] - par_range_model[which][0] );
  }

  width /= (2.35);

  if(par_prior_model[which] == GAUSSIAN)
  {
    logH -= (-0.5*pow((pm[which] - par_prior_gaussian[which][0])/par_prior_gaussian[which][1], 2.0) );
    pm[which] += dnest_randh() * width;
    dnest_wrap(&pm[which], par_range_model[which][0], par_range_model[which][1]);
    logH += (-0.5*pow((pm[which] - par_prior_gaussian[which][0])/par_prior_gaussian[which][1], 2.0) );
  }
  else
  {
    pm[which] += dnest_randh() * width;
    dnest_wrap(&(pm[which]), par_range_model[which][0], par_range_model[which][1]);
  }

  return logH;
}

/*!
 * This function calculate log likelihood probability.
 */
double log_likelihoods_cal_line1d_exam(const void *model)
{
  return 0.0;
}

void accept_action_1d()
{
  int param;
  double *ptemp;

  // the parameter previously updated
  param = which_parameter_update;
  /* continuum parameter is updated */
  if( param >= num_params_blr )
  {
    /*
     *note that (response) Fline is also changed as long as Fcon is changed.
     *num_params_blr-the parameter is the systematic error of continuum.
     *the change of this parameter also changes continuum reconstruction.
     */

    ptemp = Fcon_rm_particles[which_particle_update];
    Fcon_rm_particles[which_particle_update] = Fcon_rm_particles_perturb[which_particle_update];
    Fcon_rm_particles_perturb[which_particle_update] = ptemp;

    if(force_update != 1)
    {
      ptemp = Fline_at_data_particles[which_particle_update];
      Fline_at_data_particles[which_particle_update] = Fline_at_data_particles_perturb[which_particle_update];
      Fline_at_data_particles_perturb[which_particle_update] = ptemp;
    }
  }
  else if( param < num_params_blr-1 && force_update != 1)
  {
    /* BLR parameter is updated
     * Note a) that the (num_par_blr-1)-th parameter is systematic error of line.
     * when this parameter is updated, Trans1D and Fline are unchanged.
     *      b) Fline is always changed, except param = num_params_blr-1.
     */

    {

      ptemp = TransTau_particles[which_particle_update];
      TransTau_particles[which_particle_update] = TransTau_particles_perturb[which_particle_update];
      TransTau_particles_perturb[which_particle_update] = ptemp;

      ptemp = Trans1D_particles[which_particle_update];
      Trans1D_particles[which_particle_update] = Trans1D_particles_perturb[which_particle_update];
      Trans1D_particles_perturb[which_particle_update] = ptemp;

      ptemp = Fline_at_data_particles[which_particle_update];
      Fline_at_data_particles[which_particle_update] = Fline_at_data_particles_perturb[which_particle_update];
      Fline_at_data_particles_perturb[which_particle_update] = ptemp;
    }
  }

  return;
}

/*
 * action when particle i is killed in cdnest sampling.
 * particle i_copy's properties is copyed to particle i.
 */
void kill_action_1d(int i, int i_copy)
{
  memcpy(Fcon_rm_particles[i], Fcon_rm_particles[i_copy], parset.n_con_recon * sizeof(double));
  memcpy(Fline_at_data_particles[i], Fline_at_data_particles[i_copy], n_line_data * sizeof(double));
  memcpy(TransTau_particles[i], TransTau_particles[i_copy], parset.n_tau*sizeof(double));
  memcpy(Trans1D_particles[i], Trans1D_particles[i_copy], parset.n_tau * sizeof(double));
  return;
}


/**
 *
 * @file example_zpotrf.c
 *
 *  PLASMA testing routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @brief Example of Cholesky factorization
 *
 * @version 2.6.0
 * @author Bilel Hadri
 * @date 2010-11-15
 * @precisions normal z -> c d s
 *
 **/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <plasma.h>
#include <cblas.h>
#include <lapacke.h>
#include <core_blas.h>

int check_factorization(int, PLASMA_Complex64_t*, PLASMA_Complex64_t*, int, int);

int IONE=1;
int ISEED[4] = {0,0,0,1};   /* initial seed for zlarnv() */

int main ()
{

    int cores = 2;
    int N     = 10 ;
    int LDA   = 10 ;
    int info_factorization;

    PLASMA_Complex64_t *A1   = (PLASMA_Complex64_t *)malloc(LDA*N*sizeof(PLASMA_Complex64_t));
    PLASMA_Complex64_t *A2   = (PLASMA_Complex64_t *)malloc(LDA*N*sizeof(PLASMA_Complex64_t));

    /* Check if unable to allocate memory */
    if ((!A1)||(!A2)){
        printf("Out of Memory \n ");
        return EXIT_SUCCESS;
    }

    /* Plasma Initialize */
    PLASMA_Init(cores);
    printf("-- PLASMA is initialized to run on %d cores. \n",cores);

    /* Initialize A1 and A2 for Symmetric Positive Matrix */
    PLASMA_zplghe( (double)N, N, A1, LDA, 51 );
    PLASMA_zlacpy( PlasmaUpperLower, N, N, A1, LDA, A2, LDA );

    /* Plasma routines */
    PLASMA_zpotrf(PlasmaUpper, N, A2, LDA);

    /* Check the factorization */
    info_factorization = check_factorization( N, A1, A2, LDA, PlasmaUpper);

    if ( info_factorization != 0 )
       printf("-- Error in ZPOTRF example ! \n");
    else
       printf("-- Run of ZPOTRF example successful ! \n");

    free(A1); free(A2);

    PLASMA_Finalize();

    return EXIT_SUCCESS;
}

/*------------------------------------------------------------------------
 *  Check the factorization of the matrix A2
 */

int check_factorization(int N, PLASMA_Complex64_t *A1, PLASMA_Complex64_t *A2, int LDA, int uplo)
{
    double Anorm, Rnorm;
    PLASMA_Complex64_t alpha;
    int info_factorization;
    int i,j;
    double eps;

    eps = LAPACKE_dlamch_work('e');

    PLASMA_Complex64_t *Residual = (PLASMA_Complex64_t *)malloc(N*N*sizeof(PLASMA_Complex64_t));
    PLASMA_Complex64_t *L1       = (PLASMA_Complex64_t *)malloc(N*N*sizeof(PLASMA_Complex64_t));
    PLASMA_Complex64_t *L2       = (PLASMA_Complex64_t *)malloc(N*N*sizeof(PLASMA_Complex64_t));
    double *work              = (double *)malloc(N*sizeof(double));

    memset((void*)L1, 0, N*N*sizeof(PLASMA_Complex64_t));
    memset((void*)L2, 0, N*N*sizeof(PLASMA_Complex64_t));

    alpha= 1.0;

    LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,' ', N, N, A1, LDA, Residual, N);

    /* Dealing with L'L or U'U  */
    if (uplo == PlasmaUpper){
        LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,'u', N, N, A2, LDA, L1, N);
        LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,'u', N, N, A2, LDA, L2, N);
        cblas_ztrmm(CblasColMajor, CblasLeft, CblasUpper, CblasConjTrans, CblasNonUnit, N, N, CBLAS_SADDR(alpha), L1, N, L2, N);
    }
    else{
        LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,'l', N, N, A2, LDA, L1, N);
        LAPACKE_zlacpy_work(LAPACK_COL_MAJOR,'l', N, N, A2, LDA, L2, N);
        cblas_ztrmm(CblasColMajor, CblasRight, CblasLower, CblasConjTrans, CblasNonUnit, N, N, CBLAS_SADDR(alpha), L1, N, L2, N);
    }

    /* Compute the Residual || A -L'L|| */
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
           Residual[j*N+i] = L2[j*N+i] - Residual[j*N+i];

    Rnorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), N, N, Residual, N, work);
    Anorm = LAPACKE_zlange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), N, N, A1, LDA, work);

    printf("============\n");
    printf("Checking the Cholesky Factorization \n");
    printf("-- ||L'L-A||_oo/(||A||_oo.N.eps) = %e \n",Rnorm/(Anorm*N*eps));

    if ( isnan(Rnorm/(Anorm*N*eps)) || (Rnorm/(Anorm*N*eps) > 10.0) ){
        printf("-- Factorization is suspicious ! \n");
        info_factorization = 1;
    }
    else{
        printf("-- Factorization is CORRECT ! \n");
        info_factorization = 0;
    }

    free(Residual); free(L1); free(L2); free(work);

    return info_factorization;
}


//changes to version glm_popdyn_1.0_test2:
//lookuptable for G(x)=exp(-0.5*DT*c*exp(x/deltaV)) instead of lambda and Plam

#ifndef GLM
#define GLM 0
#endif

#ifndef GLM2
#define GLM2 2
#endif

#ifndef GLIF
#define GLIF 10
#endif

#ifndef GLIF1
#define GLIF1 11
#endif

#ifndef GLIF2
#define GLIF2 12
#endif

#ifndef GLIF4
#define GLIF4 14
#endif

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <complex.h>
#include <fftw3.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#include "util.c"

#define maxisi 2.
#define PFIRE_MAX 0.99999
#define PFIRE_MIN 0.00001

double extime;
unsigned int ntrial;

struct PopParametersGLM{
  double tref;
  double taum;
  double *taus1; //incoming synaptic time constants, decay time of 1st filter
  double *taus2; //incoming synaptic time constants, decay time of 2nd filter
  double *taur1; //incoming synaptic time constants, rise time of 1st filter
  double *taur2; //incoming synaptic time constants, rise time of 2nd filter
  double *a1;    //relative weight of fast synaptic current
  double *a2;    //relative weight of slow synaptic current
  double mu;
  double c;
  double deltaV;
  double delay;
  double vth;
  double u_reset;      //reset potential for u in glif mode
  int N;         //number of neurons in population
  double *J;     //incoming synaptic weights
  double *Iext;  //external input (in mV), i.e. Iext is actually R*I
  double *tau_theta;
  double *J_theta; //amplitudes of the N_theta exponential kernels in mV
  int N_theta;  //number of exponentials for dynamic threshold

  //internal constants
  int K;
  int end;
  int indx;
  double Em;
  double Em2;
  double *E_theta;  //Jtheta*exp(-t/tau_theta)};
  double *E_theta2;  //exp(-dt/tau_theta)};
  double *gamma_QR;  //deltaV*(1-exp(-gamma/deltaV))/N; gamma=sum_k Jtheta_k*exp(-t/tautheta_k)
  double *w1;          // effective weight
  double *w2;
  double g;
  double g2;
  double dV;  // resolution of voltage grid for lookup table of hazard/firing prob
  double Vmin;       //min value of voltage grid
  double Vmax;       //max value of voltage grid (this value is not included, i.e. max is Vmax-dv)
  double *Gtable;      //lookup table for firing prob P=1-exp(-lam*DT)
};

struct PopVariablesGLM{
  double h;
  double *u;
  double *Xsyn1; //incoming synaptic currents (filtered with fast decay time, e.g. AMPA, GABA)
  double *Xsyn2; //incoming synaptic currents (filtered with slow decay time, e.g. NMDA)
  double *Isyn1; //total incoming synaptic current (sum of Xsyn1+Xsyn2 filtered with short rise-time)
  double *Isyn2; //total incoming synaptic current (sum of Xsyn1+Xsyn2 filtered with short rise-time)
  unsigned int *n;
  double *m0;
  double *theta;  //threshold kernel
  double *g; //QR threshold variables
  double *v;
  double z;
  double x;
  double Gfree;
  double *G;
};

double DT;
double DTBIN;

////////////////////////////////////////////////////////////////////////////////

double get_mean(double *x,int n);
void print_matrix1(int N,int M, double A[][M]);
void print_matrix2(double **A,int N,int M);
void print_matrix3(int N,int M, double *A[N]);
void print_pop_parameters(struct PopParametersGLM p[], int Npop);
unsigned long int random_seed();

////////////////////////////////////////////////////////////////////////////////

void init_synaptic_filters(double **Es1, double **Es2, double **Er1, double **Er2, struct PopParametersGLM p[], int Npop)
{
  int i,j;
  for (i=0;i<Npop;i++)
    for (j=0;j<Npop;j++)
      {
	if (p[i].taur1[j]>0) Er1[i][j]=exp(-DT/p[i].taur1[j]);
	else Er1[i][j]=0;
	if (p[i].taus1[j]>0) Es1[i][j]=exp(-DT/p[i].taus1[j]);
	else Es1[i][j]=0;

	if (p[i].taur2[j]>0) Er2[i][j]=exp(-DT/p[i].taur2[j]);
	else Er2[i][j]=0;
	if (p[i].taus2[j]>0) Es2[i][j]=exp(-DT/p[i].taus2[j]);
	else Es2[i][j]=0;
      }
}

void get_inputs(double *input, int k,double **Es1,double **Es2,double **Er1,double **Er2, struct PopParametersGLM p[], struct PopVariablesGLM pop[],int Npop)
{
  int i,j,ndelay;

  double A[Npop];
  for (j=0;j<Npop;j++)
    {
      ndelay=p[j].delay/DT;
//      A[j]= pop[j].n[(p[j].end-1-ndelay+p[j].K)%p[j].K] / (p[j].N * DT);
      A[j]= pop[j].n[(p[j].end-ndelay+p[j].K)%p[j].K] / (p[j].N * DT);
    }

  for (i=0;i<Npop;i++)
    {

      //base line input
      input[i]=p[i].mu;

      //external input, assumed to be slow compared to DT
      if (p[i].Iext != NULL)
	input[i] += p[i].Iext[k] * (1-p[i].Em);  //add external input if provided

      //synaptic input
      for (j=0;j<Npop;j++)
	{
	  if (p[i].taur1[j]>0)
	    {
	      printf("Warning: rise time not correctly implemented\n");
	      input[i]+=p[i].w1[j]*pop[i].Isyn1[j];

	      if (p[i].taus1[j] > 0)
		{
		  pop[i].Isyn1[j] = pop[i].Xsyn1[j] + (pop[i].Isyn1[j] - pop[i].Xsyn1[j]) * Er1[i][j];
		  pop[i].Xsyn1[j] = A[j] + (pop[i].Xsyn1[j] - A[j]) * Es1[i][j];
		}
	      else
		  pop[i].Isyn1[j] = A[j] + (pop[i].Isyn1[j] - A[j]) * Es1[i][j];
	    }
	  else //zero-rise time
	    if (p[i].taus1[j] > 0)
	      {
		input[i] += p[i].w1[j] * (A[j] + (p[i].taus1[j] * (pop[i].Xsyn1[j] - A[j]) * Es1[i][j] - (pop[i].Xsyn1[j] * p[i].taus1[j] - A[j] * p[i].taum) * p[i].Em) / (p[i].taus1[j] - p[i].taum));

		pop[i].Xsyn1[j] = A[j] + (pop[i].Xsyn1[j] - A[j]) * Es1[i][j];
	      }
	    else //delta current
	      {
		input[i]+=p[i].w1[j]*A[j] * (1 - p[i].Em);
	      }

	  //same for 2nd filter
	  if (p[i].taur2[j]>0)
	    {
	      input[i]+=p[i].w2[j]*pop[i].Isyn2[j];

	      if (p[i].taus2[j] > 0)
		{
		  pop[i].Isyn2[j] = pop[i].Xsyn2[j] + (pop[i].Isyn2[j] - pop[i].Xsyn2[j]) * Er2[i][j];
		  pop[i].Xsyn2[j] = A[j] + (pop[i].Xsyn2[j] - A[j]) * Es2[i][j];
		}
	      else
		  pop[i].Isyn2[j] = A[j] + (pop[i].Isyn2[j] - A[j]) * Es2[i][j];
	    }
	  else //zero-rise time
	    if (p[i].taus2[j] > 0)
	      {
		input[i]+=p[i].w2[j]*pop[i].Xsyn2[j];
		pop[i].Xsyn2[j] = A[j] + (pop[i].Xsyn2[j] - A[j]) * Es2[i][j];
	      }
	    else //delta current
	      input[i]+=p[i].w2[j]*A[j];
	}

    }
}

double cond_rate(double x,struct PopParametersGLM *p)
{
  if (p->deltaV > 0) return p->c * exp(x / p->deltaV);
  else //p->D==0, no noise
    {
      if (x < 0) return 0;
      else return 1./DT;
    }
}

static double get_Plam(double V, struct PopParametersGLM *p, double *G_old)
{
  if (V<p->Vmax)
    {
      if (V>=p->Vmin)
	{
	  //int x=(int)((V - p->Vmin) / p->dV);
	  double G = p->Gtable[(int)((V - p->Vmin) / p->dV)];
	  double Plam = 1 - *G_old * G;
	  //printf("V=%g x=%d lam=%g\n",V,x,lam);
	  *G_old = G;
	  return Plam;
	}
      else
	{
	  *G_old=1;
	  return 0.;
	}
    }
  else
    {
      *G_old = 0;
      return 1.;
    }
}

void update(struct PopVariablesGLM *pop, double *nmean, double input,gsl_rng *rng,struct PopParametersGLM *p, int mode)
{

  int i,k,r;
  int kref=(int)(p->tref/DT);
  double W=0, X=0, Y=0, Z=0, theta=p->vth;
  int end=p->end, K=p->K;
  double threshold;

  // first, determine non-refractory spiking prob.
  for (r=0;r < p->N_theta; r++)
    {
      pop->g[r] = pop->g[r]*p->E_theta2[r] + (1 - p->E_theta2[r]) * pop->n[end]/(p->N * DT);
      theta += p->E_theta[r] * pop->g[r];
    }

  double Pfree;
  pop->h = input + (pop->h - p->mu) * p->Em;
  Pfree=get_Plam(pop->h-theta,p,&(pop->Gfree));

  for (k=0; k<K; k++) X += pop->m0[k];

  double ztot,Plam,PLAM, mm;
  theta -= pop->n[end] * p->gamma_QR[0];//substracts effect of n[end] above, added again in 1st loop below, but ensures that threshold theta(t+DT|k=end) is correct

  switch(mode)
    {
    case GLM:
      for (k=end;k<=end-kref+K;k++)
	{
	  i=k%K;
	  threshold = pop->theta[k-end] + theta;
	  theta += pop->n[i] * p->gamma_QR[k-end];
	  Plam=get_Plam(pop->h-threshold,p,pop->G+i);

	  Z += pop->v[i];
	  mm = Plam * pop->m0[i];
	  Y += Plam * pop->v[i];
	  W += mm;
	  pop->v[i] = (1-Plam)*(1-Plam)*pop->v[i] + mm;
	  pop->m0[i] -= mm;
	}
      break;

    case GLIF:
      {
      for (k=end;k<=end-kref+K;k++)
	{
	  i=k%K;
	  threshold = pop->theta[k-end] + theta;
	  theta += pop->n[i] * p->gamma_QR[k-end];
	  pop->u[i] = input + (pop->u[i] - p->mu) * p->Em;//GLIF
	  Plam=get_Plam(pop->u[i]-threshold,p,pop->G+i);

	  Z += pop->v[i];
	  mm = Plam * pop->m0[i];
	  Y += Plam * pop->v[i];
	  W += mm;
	  pop->v[i] = (1-Plam)*(1-Plam)*pop->v[i] + mm;
	  pop->m0[i] -= mm;

	}
      }
      pop->u[end] = p->u_reset;//GLIF
      break;
    }

  ztot = Z + pop->z;
  if (ztot>0) PLAM=(Y+Pfree*pop->z)/ztot;
  else PLAM=0;
  //  printf("W=%g Z=%g z=%g Y=%g y=%g\n",W, Z,pop->z,Y,Pfree*pop->z);

  *nmean = W + Pfree * pop->x + PLAM * (p->N - X - pop->x);
  pop->n[end] = gsl_ran_binomial(rng, *nmean/p->N, p->N);

  pop->z=(1-Pfree)*(1-Pfree)*pop->z+Pfree*pop->x+pop->v[end];
  pop->x=(1-Pfree)*pop->x+pop->m0[end];

  pop->m0[end] = pop->n[end];
  pop->v[end] = 0.;
  pop->G[end] = 1.;

}

void simulate(int Nbin,double **A,double **rate,struct PopVariablesGLM pop[],gsl_rng *rng,struct PopParametersGLM p[],int Npop, int mode, int dispprog)
{
  double input[Npop],normfact[Npop], nmean[Npop], nmean_bin[Npop];
  int n_bin[Npop];
  int L,i;
  int l=0,k=0;
  L=(int)(DTBIN/DT+0.5);
  for (i=0;i<Npop;i++)
    {
      n_bin[i]=0;
      nmean_bin[i]=0;
      normfact[i]=1./(L*DT*p[i].N);
    }

  double **Es1=dmatrix(Npop,Npop);
  double **Es2=dmatrix(Npop,Npop);
  double **Er1=dmatrix(Npop,Npop);
  double **Er2=dmatrix(Npop,Npop);
  init_synaptic_filters(Es1,Es2,Er1,Er2,p,Npop);

  int dispcount=(int)(Nbin/100);

  //  clock_t start=clock();
  while (k<Nbin)
    {
      //      printf("Nbin=%d, k=%d l=%d\n",Nbin,k,l);
      get_inputs(input,k,Es1,Es2,Er1,Er2,p,pop,Npop);

      for (i=0;i<Npop;i++)
	{
	  update(pop+i,nmean+i,input[i],rng,p+i,mode);
      	  n_bin[i]+=pop[i].n[p[i].end];
      	  nmean_bin[i]+=nmean[i];
   	}
      l++;
      //           printf("nmeanbin=%g\n",nmean_bin[0]);
	   //      printf("\n");
      if (l>=L)
      	{
      	  for (i=0;i<Npop;i++)
	    {
	      A[i][k] = n_bin[i] * normfact[i];
	      rate[i][k] = nmean_bin[i] * normfact[i];

	      //if (i==0) printf("k=%d rate=%g\n",k,rate[i][k]);
	      //	      aa[i][k]=a[i];
      	      n_bin[i]=0;
	      nmean_bin[i]=0;
	    }
      	  l=0;
      	  k++;
      	}

      for (i=0;i<Npop;i++) p[i].end=(p[i].end + 1) % p[i].K;

      if (dispprog)
      	if ((k+1)%dispcount==0)
      	  {
      	    printf("%d%%  \r",(int)((k+1) * 100. / Nbin));
      	    fflush(stdout);
      	  }

    }

  //  printf("Execution time of mesoscopic sim: %g seconds, K=%d\n",(double)(clock()-start)/CLOCKS_PER_SEC,p[0].K);

  free_dmatrix(Es1);
  free_dmatrix(Es2);
  free_dmatrix(Er1);
  free_dmatrix(Er2);

}

double threshold_kernel(double t, struct PopParametersGLM *p)
{
  double theta=0;
  int r;
  for (r=0; r<p->N_theta;r++)
    if (t>=0)  theta += p->J_theta[r] / p->tau_theta[r] * exp(-t / p->tau_theta[r]);
  return theta;
}

void init_glm(struct PopParametersGLM p[],struct PopVariablesGLM pop[],int Npop, gsl_rng *rng, int mode)
//memory allocation and initialization of variables,
{
  int i,j,k,r, K;
  for (i=0;i<Npop;i++)
    {
      K=p[i].K;
      //      printf("i=%d K=%d\n",i,K);
      pop[i].n=uivector(K);
      pop[i].m0=dvector(K);
      pop[i].v=dvector(K);
      if (mode>=GLIF) pop[i].u=dvector(K);
      else pop[i].u=NULL;
      pop[i].Isyn1=dvector(Npop);
      pop[i].Isyn2=dvector(Npop);
      pop[i].Xsyn1=dvector(Npop);
      pop[i].Xsyn2=dvector(Npop);

      p[i].Em= exp(-DT / p[i].taum);
      p[i].Em2= exp(-0.5*DT / p[i].taum);
      p[i].end = 0;

      pop[i].theta=dvector(K);
      p[i].gamma_QR=dvector(K);
      pop[i].G=dvector(K);

      for (k=0;k<K;k++)
	{
	  /* if (k<K-1)  */
	  /*   { */
	  /*     pop[i].m0[k]=(double)(p[i].N)/(K-1); //normalization (the K-1 element is set zero below) */
	  /*     pop[i].n[k]=(int)(pop[i].m0[k]+1); //initializing with spike count corresponding to zero hazard */

	  /* pop[i].n[k]=(int)(100*DT*p[i].N+1); //initializing with spike count corresponding to 100 Hz */
	  /* pop[i].m0[k]=exp(-(K-k-1)*DT*100)*pop[i].n[k]; */
	  /* n+=pop[i].m0[k]; */
	  //if (i==0) printf("m0=%g n=%d\n", pop[i].m0[k], pop[i].n[k]);
	  /*   } */

	  pop[i].m0[k]=0;
	  pop[i].n[k]=0;
	  pop[i].v[k]=0;
	  if (mode>=GLIF) pop[i].u[k]=p[i].vth-20*gsl_rng_uniform(rng);

	  pop[i].theta[k]=threshold_kernel( (K-k-1) * DT, p+i);//shifted by DT to get filter at theta(s+DT)
	  p[i].gamma_QR[k] = p[i].deltaV * (1 - exp(-pop[i].theta[k] / p[i].deltaV)) / p[i].N;
	  //	  if (i==0) printf("i=%d k=%d gamma_QR=%g\n",i,k,p[i].gamma_QR[k]);

	  pop[i].G[k]=1.;
	}
      pop[i].Gfree=1.;

      pop[i].n[K-1]=p[i].N;
      pop[i].m0[K-1]=p[i].N;
      pop[i].z=0.;
      pop[i].x=0.;
      //pop[i].x=(p[i].N - n) * DT;
      /* printf("i=%d n=%g x/dt=%g\n",i,n,pop[i].x/DT); */

      for (j=0;j<Npop;j++) pop[i].Isyn1[j]=0;
      for (j=0;j<Npop;j++) pop[i].Isyn2[j]=0;
      for (j=0;j<Npop;j++) pop[i].Xsyn1[j]=0;
      for (j=0;j<Npop;j++) pop[i].Xsyn2[j]=0;

      pop[i].h=p[i].mu;

      //pre-calculating constant J_k*exp(-tau_rel/tau_k)
      pop[i].g=dvector(p[i].N_theta);
      for (r=0; r < p[i].N_theta; r++)
	pop[i].g[r] = pop[i].n[0]/(p[i].N * DT)/p[i].tau_theta[r];

      p[i].E_theta=dvector(p[i].N_theta);
      p[i].E_theta2=dvector(p[i].N_theta);
      //      printf("E_theta= ");
      for (r=0; r < p[i].N_theta; r++)
	{
	  p[i].E_theta[r] = p[i].J_theta[r] * exp(- K * DT / p[i].tau_theta[r]);
	  p[i].E_theta2[r] = exp(- DT / p[i].tau_theta[r]);
	  //	  printf("%g tautheta=%g",p[i].E_theta[r],p[i].tau_theta[r]);
	}
      //      printf("\n");

            //pre-computing firing probability as lookup table
      p[i].dV=p[i].deltaV/100;
      p[i].Vmin = p[i].deltaV * log(-log(1-PFIRE_MIN)/DT / p[i].c);
      p[i].Vmax = p[i].deltaV * log(-log(1-PFIRE_MAX)/DT / p[i].c);
      int L=(p[i].Vmax - p[i].Vmin)/p[i].dV;
      //      printf("Lookup tbl (popul %d): Use L=%d, Vmin=%g Vmax=%g dV=%g\n",i+1,L,p[i].Vmin,p[i].Vmax,p[i].dV);
      p[i].Gtable=dvector(L);
      for (k=0;k<L;k++)
	{
	  double V=p[i].Vmin + k*p[i].dV;
	  p[i].Gtable[k]= exp(-0.5 * p[i].c * exp(V/p[i].deltaV) * DT);
	}
    }
}

void get_history_size(struct PopParametersGLM p[],int Npop)
{
  int i;
  double tmax=20.;//sec

  for (i=0;i<Npop;i++)
    {
      int k=tmax/DT;
      int kmin=5*p[i].taum/DT;
      while ((threshold_kernel(k * DT, p+i) / p[i].deltaV < 0.1) && (k>kmin)) k--;
      //      printf("K=%d\n",k);
      if (k*DT<=p[i].delay)
	k=(int)(p[i].delay/DT)+1;
      //      printf("K=%d\n",k);
      if (k*DT<=p[i].tref)
	k=(int)(p[i].tref/DT)+1;
      p[i].K=k;
      //      p[i].K=20000;
      printf("Use K=%d bins for history of population %d (T=%g sec)\n",p[i].K,i+1,p[i].K*DT);
    }
}

void free_pop(struct PopVariablesGLM pop[],struct PopParametersGLM p[],int Npop, int mode)
{
  int i;
  for (i=0;i<Npop;i++)
    {
      free_uivector(pop[i].n);
      free_dvector(pop[i].m0);
      free_dvector(pop[i].v);
      if (mode>=GLIF) free_dvector(pop[i].u);
      free_dvector(pop[i].Isyn1);
      free_dvector(pop[i].Isyn2);
      free_dvector(pop[i].Xsyn1);
      free_dvector(pop[i].Xsyn2);

      free_dvector(pop[i].g);
      free_dvector(pop[i].theta);
      free_dvector(p[i].E_theta);
      free_dvector(p[i].E_theta2);
      free_dvector(p[i].gamma_QR);
      free_dvector(pop[i].G);
      free_dvector(p[i].Gtable);
    }
}

void get_psd_pop(double **SA,int Nbin,int Ntrials,struct PopParametersGLM neuron[],int Npop, int mode)
{
  gsl_rng *rng=gsl_rng_alloc(gsl_rng_taus2);
  unsigned long int seed;
  seed = random_seed();
  gsl_rng_set(rng,seed);
  //  gsl_rng_set (rng,(long)time(NULL));

  get_history_size(neuron,Npop);

  int i,j,n;

  struct PopVariablesGLM pop[Npop];
  init_glm(neuron,pop,Npop, rng, mode);
  double sum=0;
  for (i=0;i<neuron[0].K;i++)
    {
      sum+=pop[0].m0[i];
    }

  double **A, **rate;
  A=dmatrix(Npop,Nbin);
  rate=dmatrix(Npop,Nbin);

  simulate(Nbin,A,rate,pop,rng,neuron,Npop, mode, 0);

  double complex *AF[Npop];
  for (j=0;j<Npop;j++)
    {
      AF[j]=(double complex *)malloc(sizeof(double complex)*Nbin);
      SA[j]=(double *)malloc(sizeof(double)*(Nbin/2));//psd of A
      for (i=0;i<Nbin/2;i++) SA[j][i]=0;
    }

  fftw_plan plan=fftw_plan_dft_r2c_1d(Nbin,A[0],AF[0],FFTW_ESTIMATE);

  for (n=0;n<Ntrials;n++)
    {
      //      if (n%10==9)
	{
           printf("trial %d ",n+1);
           for (j=0;j<Npop;j++) printf("%g ",get_mean(A[j],Nbin));
           printf("\r");
	   fflush(stdout);
	}
	simulate(Nbin,A,rate,pop,rng,neuron,Npop,mode, 0);

      for (j=0;j<Npop;j++)
	{
	  fftw_execute_dft_r2c(plan,A[j],AF[j]);
	  for (i=1;i<Nbin/2+1;i++) SA[j][i-1]+=creal(AF[j][i]*conj(AF[j][i]))*DTBIN/Nbin;
	}
    }
  printf("trial %d ",n);
  for (j=0;j<Npop;j++) printf("%g ",get_mean(A[j],Nbin));
  printf("\n");

  for (j=0;j<Npop;j++)
    for (i=0;i<Nbin/2;i++)  SA[j][i]/=Ntrials;

  gsl_rng_free (rng);
  free_pop(pop,neuron,Npop, mode);

  free_dmatrix(A);
  free_dmatrix(rate);

  for (j=0;j<Npop;j++) free(AF[j]);
  fftw_destroy_plan(plan);
}

void get_trajectory(double **A,double **rate,int Nbin, int Npop,struct PopParametersGLM neuron[],double dt, double dtbin, int mode, int seed)
{

  gsl_rng *rng=gsl_rng_alloc(gsl_rng_taus2);
  gsl_rng_set (rng,(long)seed);
  /* printf("seed=%d\n",seed); */
  /* printf("rndnr=%g\n",gsl_rng_uniform(rng)); */
  DT=dt;
  DTBIN=dtbin;

  get_history_size(neuron,Npop);

  struct PopVariablesGLM pop[Npop];
  init_glm(neuron,pop,Npop, rng, mode);

  clock_t start=clock();
  simulate(Nbin,A,rate,pop,rng,neuron,Npop, mode, 1);
  double sim_t=(double)(clock()-start)/CLOCKS_PER_SEC;
  printf("Execution time of mesoscopic dynamics: %g seconds, %g s per biosecond\n",sim_t,sim_t/Nbin/DTBIN);

  int i;
  //for (i=0;i<20;i++) printf("rate=%g\n",rate[0][i]);
  for (i=0;i<Npop;i++) printf("mean rate of population %d: %g \n",i+1,get_mean(rate[i],Nbin));
  //  for (i=0;i<Nbin;i++) printf("%g\n",rate[0][i]);

  gsl_rng_free (rng);
  free_pop(pop,neuron,Npop, mode);
}

void init_population(struct PopParametersGLM p[], int Npop,double tref[], double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], double **signal, int N_theta[], double J_ref[], double *J_theta[], double *tau_theta[], double sigma[])
{

  int k,l;
  for (k=0;k<Npop;k++)
    {
      p[k].indx=k;
      p[k].tref=tref[k];
      p[k].taum=taum[k];
      p[k].mu=mu[k];
      p[k].c=c[k];
      p[k].deltaV=deltaV[k];
      p[k].delay=delay[k];
      p[k].vth=vth[k];
      p[k].u_reset = vreset[k];
      p[k].N=N[k];
      p[k].g=sqrt(1-exp(-2*DT/p[k].taum)) * sigma[k];
      p[k].g2=sqrt(1-exp(-DT/p[k].taum)) * sigma[k];
      p[k].Iext=signal[k];
      p[k].taus1=taus1[k];
      p[k].taus2=taus2[k];
      p[k].taur1=taur1[k];
      p[k].taur2=taur2[k];
      if (J_ref[k]==0)
	p[k].N_theta=N_theta[k];
      else
	p[k].N_theta=N_theta[k]+1; //putting exponential refractory kernel into threshold (theta kernel)

      p[k].J_theta=dvector(p[k].N_theta);
      p[k].tau_theta=dvector(p[k].N_theta);
      for (l=0;l<N_theta[k];l++)
	{
	  p[k].J_theta[l]=J_theta[k][l];
	  p[k].tau_theta[l]=tau_theta[k][l];
	  //	  printf("k=%d l=%d J=%g tau=%g\n",k,l,J_theta[k][l],tau_theta[k][l]);
	}
      if (J_ref[k]!=0.)
	{
	  p[k].J_theta[N_theta[k]] = J_ref[k]*taum[k];
	  p[k].tau_theta[N_theta[k]] = taum[k];
	}

      //effective synaptic weights
      p[k].w1=dvector(Npop);
      p[k].w2=dvector(Npop);
      for (l=0;l<Npop;l++)
	{
	  p[k].w1[l] = J[k][l] * p_conn[k][l] * N[l] * taum[k] * a1[k][l] / (a1[k][l]+a2[k][l]);
	  p[k].w2[l] = J[k][l] * p_conn[k][l] * N[l] * taum[k] * a2[k][l] / (a1[k][l]+a2[k][l]);
	  //printf("k=%d l=%d a1=%g w1=%g\n",k,l,a1[k][l],p[k].w1[l]);
	}
    }

}

void clean_population(struct PopParametersGLM p[], int Npop)
{
  int k;
  for (k=0;k<Npop;k++) free_dvector(p[k].w1);
  for (k=0;k<Npop;k++) free_dvector(p[k].w2);
  for (k=0;k<Npop;k++) free_dvector(p[k].J_theta);
  for (k=0;k<Npop;k++) free_dvector(p[k].tau_theta);
}

void get_psd_with_fullparameterlist(double **SA, int Nbin, int Ntrials, int Npop, double *tref, double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], int N_theta[], double Jref[], double *J_theta[], double *tau_theta[], double sigma[], double dt,double dtbin, int mode)
{
  double **signal=dmatrix(Npop,1);
  int i;
  for (i=0;i<Npop;i++)
    signal[i]=NULL;

  DT=dt;
  DTBIN=dtbin;

  struct PopParametersGLM p[Npop];
  init_population(p, Npop, tref, taum, taus1, taus2, taur1, taur2, a1, a2, mu, c, deltaV, delay, vth, vreset, N, J, p_conn, signal, N_theta, Jref, J_theta, tau_theta, sigma);

  get_psd_pop(SA, Nbin, Ntrials, p, Npop, mode);
  free_dmatrix(signal);
  clean_population(p,Npop);

}

 void get_psd_with_2D_arrays(int Nf, double SA[][Nf], int Ntrials, int Npop, double *tref, double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], int N_theta[], double Jref[], double J_theta[], double tau_theta[], double sigma[], double dt,double dtbin, int mode)
{
  //convert 2D array to double**
  double *SA_tmp[Npop], *J_theta_ptr[Npop], *tau_theta_ptr[Npop];
  int i,j;
  int Nbin=2*Nf;

  int indx=0;
  for (i=0;i<Npop;i++)
    {
      J_theta_ptr[i]=&(J_theta[indx]);
      tau_theta_ptr[i]=&(tau_theta[indx]);
      indx+=N_theta[i];
      /* if (N_theta[i]>0) */
      /* 	{ */
      /* 	  J_theta_ptr[i]=&(J_theta[indx]); */
      /* 	  tau_theta_ptr[i]=&(tau_theta[indx]); */
      /* 	  indx+=N_theta[i]; */
      /* 	} */
      /* else */
      /* 	{ */
      /* 	  J_theta_ptr[i]=NULL; */
      /* 	  tau_theta_ptr[i]=NULL; */
      /* 	} */
    }

  get_psd_with_fullparameterlist(SA_tmp, Nbin, Ntrials, Npop, tref, taum, taus1, taus2, taur1, taur2, a1, a2, mu, c, deltaV, delay, vth, vreset, N, J, p_conn, N_theta, Jref, J_theta_ptr, tau_theta_ptr, sigma, dt, dtbin, mode);

  for (j=0;j<Npop;j++)
    for (i=0;i<Nf;i++)
      SA[j][i]=SA_tmp[j][i];

  for (j=0;j<Npop;j++)
    free(SA_tmp[j]);

}

void get_trajectory_with_fullparameterlist(double **A, double **rate, int Nbin, int Npop, double *tref, double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], double **signal, int N_theta[], double Jref[], double *J_theta[], double *tau_theta[], double sigma[], double dt,double dtbin, int mode, int seed)
{
  DT=dt;
  DTBIN=dtbin;

  struct PopParametersGLM p[Npop];
  init_population(p, Npop, tref, taum, taus1, taus2, taur1, taur2, a1, a2, mu, c, deltaV, delay, vth, vreset, N, J, p_conn, signal, N_theta, Jref, J_theta, tau_theta, sigma);
  //  print_pop_parameters(p,Npop);

  get_trajectory(A, rate, Nbin, Npop, p, dt, dtbin, mode, seed);

  clean_population(p,Npop);
}

void get_trajectory_with_2D_arrays(int Nbin, double A[][Nbin], double rate[][Nbin], int Npop, double *tref, double taum[], double taus1[][Npop], double taus2[][Npop], double taur1[][Npop], double taur2[][Npop], double a1[][Npop], double a2[][Npop], double mu[], double c[], double deltaV[], double delay[], double vth[], double vreset[], int N[], double J[][Npop], double p_conn[][Npop], double s[][Nbin], int N_theta[], double Jref[], double J_theta[], double tau_theta[], double sigma[], double dt,double dtbin, int mode, int seed)
{

  //convert 2D array to double**
  double *rate_ptr[Npop],*AA[Npop],*signal[Npop], *J_theta_ptr[Npop], *tau_theta_ptr[Npop];
  int i;

  for (i=0;i<Npop;i++)
    AA[i]=A[i];
  for (i=0;i<Npop;i++)
    rate_ptr[i]=rate[i];
  for (i=0;i<Npop;i++)
    signal[i]=s[i];

  int indx=0;
  for (i=0;i<Npop;i++)
    {
      if (N_theta[i]>0)
  	{
  	  J_theta_ptr[i]=&(J_theta[indx]);
  	  tau_theta_ptr[i]=&(tau_theta[indx]);
  	  indx+=N_theta[i];
  	}
      else
  	{
  	  J_theta_ptr[i]=NULL;
  	  tau_theta_ptr[i]=NULL;
  	}
    }

  get_trajectory_with_fullparameterlist(AA, rate_ptr, Nbin, Npop, tref, taum, taus1, taus2, taur1, taur2, a1, a2, mu, c, deltaV, delay, vth, vreset, N, J, p_conn, signal, N_theta, Jref, J_theta_ptr, tau_theta_ptr, sigma, dt, dtbin, mode, seed);

}

//===================================================================
// SRM Model
//===================================================================

void print_pop_parameters(struct PopParametersGLM p[], int Npop)
{

  int k,l;
  printf("\n");
  for (k=0;k<Npop;k++)
    {
      printf("POPULATION %d\n",k+1);
      printf("tref=%g\n",p[k].tref);
      printf("taum=%g\n",p[k].taum);
      printf("mu=%g\n",p[k].mu);
      printf("c=%g\n",p[k].c);
      printf("DeltaV=%g\n",p[k].deltaV);
      printf("delay=%g\n",p[k].delay);
      printf("vth=%g\n",p[k].vth);
      printf("vreset=%g\n",p[k].u_reset);
      printf("N=%d\n",p[k].N);
      printf("Ntheta=%d\n",p[k].N_theta);
      for (l=0;l<p[k].N_theta;l++)
	{
	  printf("Jtheta=%g\n",p[k].J_theta[l]);
	  printf("tautheta=%g\n",p[k].tau_theta[l]);
	}
      for (l=0;l<Npop;l++)
	{
	  printf("pop%d to pop%d:\n",l+1,k+1);
	  printf("   w1=%g\n",p[k].w1[l]);
	  printf("   w2=%g\n",p[k].w2[l]);
	  printf("   taus1=%g taur1=%g\n",p[k].taus1[l],p[k].taur1[l]);
	  printf("   taus2=%g taur2=%g\n",p[k].taus2[l],p[k].taur2[l]);
	}
      printf("\n");
    }
  printf("DT=%g DTBIN=%g\n",DT,DTBIN);
}

double get_mean(double *x,int n)
{
  int i;
  double m=0;
  if (n>200) //discard initial transient
    {
      for (i=200;i<n;i++) m+=x[i];
      return m/(n-200);
    }
  else
    {
      for (i=0;i<n;i++) m+=x[i];
      return m/n;
    }
}

void print_matrix1(int N,int M, double A[][M])
{
  int i,j;
  for (i=0;i<N;i++)
    {
      for (j=0;j<M;j++)
	printf("%g ",A[i][j]);
      printf("\n");
    }
}

void print_matrix2(double **A,int N,int M)
{
  int i,j;
  for (i=0;i<N;i++)
    {
      for (j=0;j<M;j++)
	printf("%g ",A[i][j]);
      printf("\n");
    }
}

void print_matrix3(int N,int M, double *A[N])
{
  int i,j;
  for (i=0;i<N;i++)
    {
      for (j=0;j<M;j++)
	printf("%g ",A[i][j]);
      printf("\n");
    }
}

/* unsigned long int random_seed() */
/* { */

/*  unsigned int seed; */
/*  FILE *devrandom; */

/*  if ((devrandom = fopen("/dev/random","r")) == NULL) { */
/*    fprintf(stderr,"Cannot open /dev/random, setting seed to 0\n"); */
/*    seed = 0; */
/*  } else { */
/*    fread(&seed,sizeof(seed),1,devrandom); */
/*    if(verbose == D_SEED) printf("Got seed %u from /dev/random\n",seed); */
/*    fclose(devrandom); */
/*  } */

/*  return(seed); */

/* } */

#include <sys/time.h>
unsigned long int random_seed()
{

 unsigned int seed;
 struct timeval tv;
 FILE *devrandom;

 if ((devrandom = fopen("/dev/urandom","r")) == NULL) {
   gettimeofday(&tv,0);
   seed = tv.tv_sec + tv.tv_usec;
 } else {
   fread(&seed,sizeof(seed),1,devrandom);
   fclose(devrandom);
 }

 return(seed);

}