/*
 * This file is part of the GROMACS molecular simulation package.
 *
 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 * Copyright (c) 2001-2004, The GROMACS development team,
 * check out http://www.gromacs.org for more information.
 * Copyright (c) 2012,2013, by the GROMACS development team, led by
 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 * others, as listed in the AUTHORS file in the top-level source
 * directory and at http://www.gromacs.org.
 *
 * GROMACS is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * GROMACS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GROMACS; if not, see
 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 *
 * If you want to redistribute modifications to GROMACS, please
 * consider that scientific software is very special. Version
 * control is crucial - bugs must be traceable. We will be happy to
 * consider code for inclusion in the official distribution, but
 * derived work must not be called official GROMACS. Details are found
 * in the README & COPYING files - if they are missing, get the
 * official version at http://www.gromacs.org.
 *
 * To help us fund GROMACS development, we humbly ask that you cite
 * the research papers on the package. Check out http://www.gromacs.org.
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "statutil.h"
#include "sysstuff.h"
#include "typedefs.h"
#include "smalloc.h"
#include "macros.h"
#include "gmx_fatal.h"
#include "vec.h"
#include "copyrite.h"
#include "futil.h"
#include "readinp.h"
#include "statutil.h"
#include "txtdump.h"
#include "gstat.h"
#include "xvgr.h"
#include "physics.h"
#include "gmx_ana.h"

enum {
    epAuf, epEuf, epAfu, epEfu, epNR
};
enum {
    eqAif, eqEif, eqAfi, eqEfi, eqAui, eqEui, eqAiu, eqEiu, eqNR
};
static char *eep[epNR] = { "Af", "Ef", "Au", "Eu" };
static char *eeq[eqNR] = { "Aif", "Eif", "Afi", "Efi", "Aui", "Eui", "Aiu", "Eiu" };

typedef struct {
    int       nreplica;  /* Number of replicas in the calculation                   */
    int       nframe;    /* Number of time frames                                   */
    int       nstate;    /* Number of states the system can be in, e.g. F,I,U       */
    int       nparams;   /* Is 2, 4 or 8                                            */
    gmx_bool *bMask;     /* Determine whether this replica is part of the d2 comp.  */
    gmx_bool  bSum;
    gmx_bool  bDiscrete; /* Use either discrete folding (0/1) or a continuous       */
    /* criterion */
    int       nmask;     /* Number of replicas taken into account                   */
    real      dt;        /* Timestep between frames                                 */
    int       j0, j1;    /* Range of frames used in calculating delta               */
    real    **temp, **data, **data2;
    int     **state;     /* State index running from 0 (F) to nstate-1 (U)          */
    real    **beta, **fcalt, **icalt;
    real     *time, *sumft, *sumit, *sumfct, *sumict;
    real     *params;
    real     *d2_replica;
} t_remd_data;

#ifdef HAVE_LIBGSL
#include <gsl/gsl_multimin.h>

static char *itoa(int i)
{
    static char ptr[12];

    sprintf(ptr, "%d", i);
    return ptr;
}

static char *epnm(int nparams, int index)
{
    static char buf[32], from[8], to[8];
    int         nn, ni, ii;

    range_check(index, 0, nparams);
    if ((nparams == 2) || (nparams == 4))
    {
        return eep[index];
    }
    else if ((nparams > 4) && (nparams % 4 == 0))
    {
        return eeq[index];
    }
    else
    {
        gmx_fatal(FARGS, "Don't know how to handle %d parameters", nparams);
    }

    return NULL;
}

static gmx_bool bBack(t_remd_data *d)
{
    return (d->nparams > 2);
}

static real is_folded(t_remd_data *d, int irep, int jframe)
{
    if (d->state[irep][jframe] == 0)
    {
        return 1.0;
    }
    else
    {
        return 0.0;
    }
}

static real is_unfolded(t_remd_data *d, int irep, int jframe)
{
    if (d->state[irep][jframe] == d->nstate-1)
    {
        return 1.0;
    }
    else
    {
        return 0.0;
    }
}

static real is_intermediate(t_remd_data *d, int irep, int jframe)
{
    if ((d->state[irep][jframe] == 1) && (d->nstate > 2))
    {
        return 1.0;
    }
    else
    {
        return 0.0;
    }
}

static void integrate_dfdt(t_remd_data *d)
{
    int    i, j;
    double beta, ddf, ddi, df, db, fac, sumf, sumi, area;

    d->sumfct[0] = 0;
    d->sumict[0] = 0;
    for (i = 0; (i < d->nreplica); i++)
    {
        if (d->bMask[i])
        {
            if (d->bDiscrete)
            {
                ddf = 0.5*d->dt*is_folded(d, i, 0);
                ddi = 0.5*d->dt*is_intermediate(d, i, 0);
            }
            else
            {
                ddf = 0.5*d->dt*d->state[i][0];
                ddi = 0.0;
            }
            d->fcalt[i][0] = ddf;
            d->icalt[i][0] = ddi;
            d->sumfct[0]  += ddf;
            d->sumict[0]  += ddi;
        }
    }
    for (j = 1; (j < d->nframe); j++)
    {
        if (j == d->nframe-1)
        {
            fac = 0.5*d->dt;
        }
        else
        {
            fac = d->dt;
        }
        sumf = sumi = 0;
        for (i = 0; (i < d->nreplica); i++)
        {
            if (d->bMask[i])
            {
                beta = d->beta[i][j];
                if ((d->nstate <= 2) || d->bDiscrete)
                {
                    if (d->bDiscrete)
                    {
                        df = (d->params[epAuf]*exp(-beta*d->params[epEuf])*
                              is_unfolded(d, i, j));
                    }
                    else
                    {
                        area = (d->data2 ? d->data2[i][j] : 1.0);
                        df   =  area*d->params[epAuf]*exp(-beta*d->params[epEuf]);
                    }
                    if (bBack(d))
                    {
                        db = 0;
                        if (d->bDiscrete)
                        {
                            db = (d->params[epAfu]*exp(-beta*d->params[epEfu])*
                                  is_folded(d, i, j));
                        }
                        else
                        {
                            gmx_fatal(FARGS, "Back reaction not implemented with continuous");
                        }
                        ddf = fac*(df-db);
                    }
                    else
                    {
                        ddf = fac*df;
                    }
                    d->fcalt[i][j] = d->fcalt[i][j-1] + ddf;
                    sumf          += ddf;
                }
                else
                {
                    ddf = fac*((d->params[eqAif]*exp(-beta*d->params[eqEif])*
                                is_intermediate(d, i, j)) -
                               (d->params[eqAfi]*exp(-beta*d->params[eqEfi])*
                                is_folded(d, i, j)));
                    ddi = fac*((d->params[eqAui]*exp(-beta*d->params[eqEui])*
                                is_unfolded(d, i, j)) -
                               (d->params[eqAiu]*exp(-beta*d->params[eqEiu])*
                                is_intermediate(d, i, j)));
                    d->fcalt[i][j] = d->fcalt[i][j-1] + ddf;
                    d->icalt[i][j] = d->icalt[i][j-1] + ddi;
                    sumf          += ddf;
                    sumi          += ddi;
                }
            }
        }
        d->sumfct[j] = d->sumfct[j-1] + sumf;
        d->sumict[j] = d->sumict[j-1] + sumi;
    }
    if (debug)
    {
        fprintf(debug, "@type xy\n");
        for (j = 0; (j < d->nframe); j++)
        {
            fprintf(debug, "%8.3f  %12.5e\n", d->time[j], d->sumfct[j]);
        }
        fprintf(debug, "&\n");
    }
}

static void sum_ft(t_remd_data *d)
{
    int    i, j;
    double fac;

    for (j = 0; (j < d->nframe); j++)
    {
        d->sumft[j] = 0;
        d->sumit[j] = 0;
        if ((j == 0) || (j == d->nframe-1))
        {
            fac = d->dt*0.5;
        }
        else
        {
            fac = d->dt;
        }
        for (i = 0; (i < d->nreplica); i++)
        {
            if (d->bMask[i])
            {
                if (d->bDiscrete)
                {
                    d->sumft[j] += fac*is_folded(d, i, j);
                    d->sumit[j] += fac*is_intermediate(d, i, j);
                }
                else
                {
                    d->sumft[j] += fac*d->state[i][j];
                }
            }
        }
    }
}

static double calc_d2(t_remd_data *d)
{
    int    i, j;
    double dd2, d2 = 0, dr2, tmp;

    integrate_dfdt(d);

    if (d->bSum)
    {
        for (j = d->j0; (j < d->j1); j++)
        {
            if (d->bDiscrete)
            {
                d2  += sqr(d->sumft[j]-d->sumfct[j]);
                if (d->nstate > 2)
                {
                    d2 += sqr(d->sumit[j]-d->sumict[j]);
                }
            }
            else
            {
                d2  += sqr(d->sumft[j]-d->sumfct[j]);
            }
        }
    }
    else
    {
        for (i = 0; (i < d->nreplica); i++)
        {
            dr2 = 0;
            if (d->bMask[i])
            {
                for (j = d->j0; (j < d->j1); j++)
                {
                    tmp  = sqr(is_folded(d, i, j)-d->fcalt[i][j]);
                    d2  += tmp;
                    dr2 += tmp;
                    if (d->nstate > 2)
                    {
                        tmp  = sqr(is_intermediate(d, i, j)-d->icalt[i][j]);
                        d2  += tmp;
                        dr2 += tmp;
                    }
                }
                d->d2_replica[i] = dr2/(d->j1-d->j0);
            }
        }
    }
    dd2 = (d2/(d->j1-d->j0))/(d->bDiscrete ? d->nmask : 1);

    return dd2;
}

static double my_f(const gsl_vector *v, void *params)
{
    t_remd_data *d       = (t_remd_data *) params;
    double       penalty = 0;
    int          i;

    for (i = 0; (i < d->nparams); i++)
    {
        d->params[i] = gsl_vector_get(v, i);
        if (d->params[i] < 0)
        {
            penalty += 10;
        }
    }
    if (penalty > 0)
    {
        return penalty;
    }
    else
    {
        return calc_d2(d);
    }
}

static void optimize_remd_parameters(FILE *fp, t_remd_data *d, int maxiter,
                                     real tol)
{
    real   size, d2;
    int    iter   = 0;
    int    status = 0;
    int    i;

    const gsl_multimin_fminimizer_type *T;
    gsl_multimin_fminimizer            *s;

    gsl_vector                         *x, *dx;
    gsl_multimin_function               my_func;

    my_func.f      = &my_f;
    my_func.n      = d->nparams;
    my_func.params = (void *) d;

    /* Starting point */
    x = gsl_vector_alloc (my_func.n);
    for (i = 0; (i < my_func.n); i++)
    {
        gsl_vector_set (x, i, d->params[i]);
    }

    /* Step size, different for each of the parameters */
    dx = gsl_vector_alloc (my_func.n);
    for (i = 0; (i < my_func.n); i++)
    {
        gsl_vector_set (dx, i, 0.1*d->params[i]);
    }

    T = gsl_multimin_fminimizer_nmsimplex;
    s = gsl_multimin_fminimizer_alloc (T, my_func.n);

    gsl_multimin_fminimizer_set (s, &my_func, x, dx);
    gsl_vector_free (x);
    gsl_vector_free (dx);

    printf ("%5s", "Iter");
    for (i = 0; (i < my_func.n); i++)
    {
        printf(" %12s", epnm(my_func.n, i));
    }
    printf (" %12s %12s\n", "NM Size", "Chi2");

    do
    {
        iter++;
        status = gsl_multimin_fminimizer_iterate (s);

        if (status != 0)
        {
            gmx_fatal(FARGS, "Something went wrong in the iteration in minimizer %s",
                      gsl_multimin_fminimizer_name(s));
        }

        d2     = gsl_multimin_fminimizer_minimum(s);
        size   = gsl_multimin_fminimizer_size(s);
        status = gsl_multimin_test_size(size, tol);

        if (status == GSL_SUCCESS)
        {
            printf ("Minimum found using %s at:\n",
                    gsl_multimin_fminimizer_name(s));
        }

        printf ("%5d", iter);
        for (i = 0; (i < my_func.n); i++)
        {
            printf(" %12.4e", gsl_vector_get (s->x, i));
        }
        printf (" %12.4e %12.4e\n", size, d2);
    }
    while ((status == GSL_CONTINUE) && (iter < maxiter));

    gsl_multimin_fminimizer_free (s);
}

static void preprocess_remd(FILE *fp, t_remd_data *d, real cutoff, real tref,
                            real ucut, gmx_bool bBack, real Euf, real Efu,
                            real Ei, real t0, real t1, gmx_bool bSum, gmx_bool bDiscrete,
                            int nmult)
{
    int  i, j, ninter;
    real dd, tau_f, tau_u;

    ninter = (ucut > cutoff) ? 1 : 0;
    if (ninter && (ucut <= cutoff))
    {
        gmx_fatal(FARGS, "You have requested an intermediate but the cutoff for intermediates %f is smaller than the normal cutoff(%f)", ucut, cutoff);
    }

    if (!bBack)
    {
        d->nparams = 2;
        d->nstate  = 2;
    }
    else
    {
        d->nparams = 4*(1+ninter);
        d->nstate  = 2+ninter;
    }
    d->bSum      = bSum;
    d->bDiscrete = bDiscrete;
    snew(d->beta, d->nreplica);
    snew(d->state, d->nreplica);
    snew(d->bMask, d->nreplica);
    snew(d->d2_replica, d->nreplica);
    snew(d->sumft, d->nframe);
    snew(d->sumit, d->nframe);
    snew(d->sumfct, d->nframe);
    snew(d->sumict, d->nframe);
    snew(d->params, d->nparams);
    snew(d->fcalt, d->nreplica);
    snew(d->icalt, d->nreplica);

    /* convert_times(d->nframe,d->time); */

    if (t0 < 0)
    {
        d->j0 = 0;
    }
    else
    {
        for (d->j0 = 0; (d->j0 < d->nframe) && (d->time[d->j0] < t0); d->j0++)
        {
            ;
        }
    }
    if (t1 < 0)
    {
        d->j1 = d->nframe;
    }
    else
    {
        for (d->j1 = 0; (d->j1 < d->nframe) && (d->time[d->j1] < t1); d->j1++)
        {
            ;
        }
    }
    if ((d->j1-d->j0) < d->nparams+2)
    {
        gmx_fatal(FARGS, "Start (%f) or end time (%f) for fitting inconsistent. Reduce t0, increase t1 or supply more data", t0, t1);
    }
    fprintf(fp, "Will optimize from %g to %g\n",
            d->time[d->j0], d->time[d->j1-1]);
    d->nmask = d->nreplica;
    for (i = 0; (i < d->nreplica); i++)
    {
        snew(d->beta[i], d->nframe);
        snew(d->state[i], d->nframe);
        snew(d->fcalt[i], d->nframe);
        snew(d->icalt[i], d->nframe);
        d->bMask[i] = TRUE;
        for (j = 0; (j < d->nframe); j++)
        {
            d->beta[i][j] = 1.0/(BOLTZ*d->temp[i][j]);
            dd            = d->data[i][j];
            if (bDiscrete)
            {
                if (dd <= cutoff)
                {
                    d->state[i][j] = 0;
                }
                else if ((ucut > cutoff) && (dd <= ucut))
                {
                    d->state[i][j] = 1;
                }
                else
                {
                    d->state[i][j] = d->nstate-1;
                }
            }
            else
            {
                d->state[i][j] = dd*nmult;
            }
        }
    }
    sum_ft(d);

    /* Assume forward rate constant is half the total time in this
     * simulation and backward is ten times as long */
    if (bDiscrete)
    {
        tau_f            = d->time[d->nframe-1];
        tau_u            = 4*tau_f;
        d->params[epEuf] = Euf;
        d->params[epAuf] = exp(d->params[epEuf]/(BOLTZ*tref))/tau_f;
        if (bBack)
        {
            d->params[epEfu] = Efu;
            d->params[epAfu] = exp(d->params[epEfu]/(BOLTZ*tref))/tau_u;
            if (ninter > 0)
            {
                d->params[eqEui] = Ei;
                d->params[eqAui] = exp(d->params[eqEui]/(BOLTZ*tref))/tau_u;
                d->params[eqEiu] = Ei;
                d->params[eqAiu] = exp(d->params[eqEiu]/(BOLTZ*tref))/tau_u;
            }
        }
        else
        {
            d->params[epAfu]  = 0;
            d->params[epEfu]  = 0;
        }
    }
    else
    {
        d->params[epEuf] = Euf;
        if (d->data2)
        {
            d->params[epAuf] = 0.1;
        }
        else
        {
            d->params[epAuf] = 20.0;
        }
    }
}

static real tau(real A, real E, real T)
{
    return exp(E/(BOLTZ*T))/A;
}

static real folded_fraction(t_remd_data *d, real tref)
{
    real tauf, taub;

    tauf = tau(d->params[epAuf], d->params[epEuf], tref);
    taub = tau(d->params[epAfu], d->params[epEfu], tref);

    return (taub/(tauf+taub));
}

static void print_tau(FILE *gp, t_remd_data *d, real tref)
{
    real tauf, taub, ddd, fff, DG, DH, TDS, Tm, Tb, Te, Fb, Fe, Fm;
    int  i, np = d->nparams;

    ddd = calc_d2(d);
    fprintf(gp, "Final value for Chi2 = %12.5e (%d replicas)\n", ddd, d->nmask);
    tauf = tau(d->params[epAuf], d->params[epEuf], tref);
    fprintf(gp, "%s = %12.5e %s = %12.5e (kJ/mole)\n",
            epnm(np, epAuf), d->params[epAuf],
            epnm(np, epEuf), d->params[epEuf]);
    if (bBack(d))
    {
        taub = tau(d->params[epAfu], d->params[epEfu], tref);
        fprintf(gp, "%s = %12.5e %s = %12.5e (kJ/mole)\n",
                epnm(np, epAfu), d->params[epAfu],
                epnm(np, epEfu), d->params[epEfu]);
        fprintf(gp, "Equilibrium properties at T = %g\n", tref);
        fprintf(gp, "tau_f = %8.3f ns, tau_b = %8.3f ns\n", tauf/1000, taub/1000);
        fff = taub/(tauf+taub);
        DG  = BOLTZ*tref*log(fff/(1-fff));
        DH  = d->params[epEfu]-d->params[epEuf];
        TDS = DH-DG;
        fprintf(gp, "Folded fraction     F = %8.3f\n", fff);
        fprintf(gp, "Unfolding energies: DG = %8.3f  DH = %8.3f TDS = %8.3f\n",
                DG, DH, TDS);
        Tb = 260;
        Te = 420;
        Tm = 0;
        Fm = 0;
        Fb = folded_fraction(d, Tb);
        Fe = folded_fraction(d, Te);
        while ((Te-Tb > 0.001) && (Fm != 0.5))
        {
            Tm = 0.5*(Tb+Te);
            Fm = folded_fraction(d, Tm);
            if (Fm > 0.5)
            {
                Fb = Fm;
                Tb = Tm;
            }
            else if (Fm < 0.5)
            {
                Te = Tm;
                Fe = Fm;
            }
        }
        if ((Fb-0.5)*(Fe-0.5) <= 0)
        {
            fprintf(gp, "Melting temperature Tm = %8.3f K\n", Tm);
        }
        else
        {
            fprintf(gp, "No melting temperature detected between 260 and 420K\n");
        }
        if (np > 4)
        {
            char *ptr;
            fprintf(gp, "Data for intermediates at T = %g\n", tref);
            fprintf(gp, "%8s  %10s  %10s  %10s\n", "Name", "A", "E", "tau");
            for (i = 0; (i < np/2); i++)
            {
                tauf = tau(d->params[2*i], d->params[2*i+1], tref);
                ptr  = epnm(d->nparams, 2*i);
                fprintf(gp, "%8s  %10.3e  %10.3e  %10.3e\n", ptr+1,
                        d->params[2*i], d->params[2*i+1], tauf/1000);
            }
        }
    }
    else
    {
        fprintf(gp, "Equilibrium properties at T = %g\n", tref);
        fprintf(gp, "tau_f = %8.3f\n", tauf);
    }
}

static void dump_remd_parameters(FILE *gp, t_remd_data *d, const char *fn,
                                 const char *fn2, const char *rfn,
                                 const char *efn, const char *mfn, int skip, real tref,
                                 output_env_t oenv)
{
    FILE       *fp, *hp;
    int         i, j, np = d->nparams;
    real        rhs, tauf, taub, fff, DG;
    real       *params;
    const char *leg[]  = { "Measured", "Fit", "Difference" };
    const char *mleg[] = { "Folded fraction", "DG (kJ/mole)"};
    char      **rleg;
    real        fac[] = { 0.97, 0.98, 0.99, 1.0, 1.01, 1.02, 1.03 };
#define NFAC asize(fac)
    real        d2[NFAC];
    double      norm;

    integrate_dfdt(d);
    print_tau(gp, d, tref);
    norm = (d->bDiscrete ? 1.0/d->nmask : 1.0);

    if (fn)
    {
        fp = xvgropen(fn, "Optimized fit to data", "Time (ps)", "Fraction Folded", oenv);
        xvgr_legend(fp, asize(leg), leg, oenv);
        for (i = 0; (i < d->nframe); i++)
        {
            if ((skip <= 0) || ((i % skip) == 0))
            {
                fprintf(fp, "%12.5e  %12.5e  %12.5e  %12.5e\n", d->time[i],
                        d->sumft[i]*norm, d->sumfct[i]*norm,
                        (d->sumft[i]-d->sumfct[i])*norm);
            }
        }
        ffclose(fp);
    }
    if (!d->bSum && rfn)
    {
        snew(rleg, d->nreplica*2);
        for (i = 0; (i < d->nreplica); i++)
        {
            snew(rleg[2*i], 32);
            snew(rleg[2*i+1], 32);
            sprintf(rleg[2*i], "\\f{4}F(t) %d", i);
            sprintf(rleg[2*i+1], "\\f{12}F \\f{4}(t) %d", i);
        }
        fp = xvgropen(rfn, "Optimized fit to data", "Time (ps)", "Fraction Folded", oenv);
        xvgr_legend(fp, d->nreplica*2, (const char**)rleg, oenv);
        for (j = 0; (j < d->nframe); j++)
        {
            if ((skip <= 0) || ((j % skip) == 0))
            {
                fprintf(fp, "%12.5e", d->time[j]);
                for (i = 0; (i < d->nreplica); i++)
                {
                    fprintf(fp, "  %5f  %9.2e", is_folded(d, i, j), d->fcalt[i][j]);
                }
                fprintf(fp, "\n");
            }
        }
        ffclose(fp);
    }

    if (fn2 && (d->nstate > 2))
    {
        fp = xvgropen(fn2, "Optimized fit to data", "Time (ps)",
                      "Fraction Intermediate", oenv);
        xvgr_legend(fp, asize(leg), leg, oenv);
        for (i = 0; (i < d->nframe); i++)
        {
            if ((skip <= 0) || ((i % skip) == 0))
            {
                fprintf(fp, "%12.5e  %12.5e  %12.5e  %12.5e\n", d->time[i],
                        d->sumit[i]*norm, d->sumict[i]*norm,
                        (d->sumit[i]-d->sumict[i])*norm);
            }
        }
        ffclose(fp);
    }
    if (mfn)
    {
        if (bBack(d))
        {
            fp = xvgropen(mfn, "Melting curve", "T (K)", "", oenv);
            xvgr_legend(fp, asize(mleg), mleg, oenv);
            for (i = 260; (i <= 420); i++)
            {
                tauf = tau(d->params[epAuf], d->params[epEuf], 1.0*i);
                taub = tau(d->params[epAfu], d->params[epEfu], 1.0*i);
                fff  = taub/(tauf+taub);
                DG   = BOLTZ*i*log(fff/(1-fff));
                fprintf(fp, "%5d  %8.3f  %8.3f\n", i, fff, DG);
            }
            ffclose(fp);
        }
    }

    if (efn)
    {
        snew(params, d->nparams);
        for (i = 0; (i < d->nparams); i++)
        {
            params[i] = d->params[i];
        }

        hp = xvgropen(efn, "Chi2 as a function of relative parameter",
                      "Fraction", "Chi2", oenv);
        for (j = 0; (j < d->nparams); j++)
        {
            /* Reset all parameters to optimized values */
            fprintf(hp, "@type xy\n");
            for (i = 0; (i < d->nparams); i++)
            {
                d->params[i] = params[i];
            }
            /* Now modify one of them */
            for (i = 0; (i < NFAC); i++)
            {
                d->params[j] = fac[i]*params[j];
                d2[i]        = calc_d2(d);
                fprintf(gp, "%s = %12g  d2 = %12g\n", epnm(np, j), d->params[j], d2[i]);
                fprintf(hp, "%12g  %12g\n", fac[i], d2[i]);
            }
            fprintf(hp, "&\n");
        }
        ffclose(hp);
        for (i = 0; (i < d->nparams); i++)
        {
            d->params[i] = params[i];
        }
        sfree(params);
    }
    if (!d->bSum)
    {
        for (i = 0; (i < d->nreplica); i++)
        {
            fprintf(gp, "Chi2[%3d] = %8.2e\n", i, d->d2_replica[i]);
        }
    }
}
#endif /*HAVE_LIBGSL*/

int gmx_kinetics(int argc, char *argv[])
{
    const char     *desc[] = {
        "[TT]g_kinetics[tt] reads two [TT].xvg[tt] files, each one containing data for N replicas.",
        "The first file contains the temperature of each replica at each timestep,",
        "and the second contains real values that can be interpreted as",
        "an indicator for folding. If the value in the file is larger than",
        "the cutoff it is taken to be unfolded and the other way around.[PAR]",
        "From these data an estimate of the forward and backward rate constants",
        "for folding is made at a reference temperature. In addition,",
        "a theoretical melting curve and free energy as a function of temperature",
        "are printed in an [TT].xvg[tt] file.[PAR]",
        "The user can give a max value to be regarded as intermediate",
        "([TT]-ucut[tt]), which, when given will trigger the use of an intermediate state",
        "in the algorithm to be defined as those structures that have",
        "cutoff < DATA < ucut. Structures with DATA values larger than ucut will",
        "not be regarded as potential folders. In this case 8 parameters are optimized.[PAR]",
        "The average fraction foled is printed in an [TT].xvg[tt] file together with the fit to it.",
        "If an intermediate is used a further file will show the build of the intermediate and the fit to that process.[PAR]",
        "The program can also be used with continuous variables (by setting",
        "[TT]-nodiscrete[tt]). In this case kinetics of other processes can be",
        "studied. This is very much a work in progress and hence the manual",
        "(this information) is lagging behind somewhat.[PAR]",
        "In order to compile this program you need access to the GNU",
        "scientific library."
    };
    static int      nreplica  = 1;
    static real     tref      = 298.15;
    static real     cutoff    = 0.2;
    static real     ucut      = 0.0;
    static real     Euf       = 10;
    static real     Efu       = 30;
    static real     Ei        = 10;
    static gmx_bool bHaveT    = TRUE;
    static real     t0        = -1;
    static real     t1        = -1;
    static real     tb        = 0;
    static real     te        = 0;
    static real     tol       = 1e-3;
    static int      maxiter   = 100;
    static int      skip      = 0;
    static int      nmult     = 1;
    static gmx_bool bBack     = TRUE;
    static gmx_bool bSplit    = TRUE;
    static gmx_bool bSum      = TRUE;
    static gmx_bool bDiscrete = TRUE;
    t_pargs         pa[]      = {
        { "-time",    FALSE, etBOOL, {&bHaveT},
          "Expect a time in the input" },
        { "-b",       FALSE, etREAL, {&tb},
          "First time to read from set" },
        { "-e",       FALSE, etREAL, {&te},
          "Last time to read from set" },
        { "-bfit",    FALSE, etREAL, {&t0},
          "Time to start the fit from" },
        { "-efit",    FALSE, etREAL, {&t1},
          "Time to end the fit" },
        { "-T",       FALSE, etREAL, {&tref},
          "Reference temperature for computing rate constants" },
        { "-n",       FALSE, etINT, {&nreplica},
          "Read data for this number of replicas. Only necessary when files are written in xmgrace format using @type and & as delimiters." },
        { "-cut",     FALSE, etREAL, {&cutoff},
          "Cut-off (max) value for regarding a structure as folded" },
        { "-ucut",    FALSE, etREAL, {&ucut},
          "Cut-off (max) value for regarding a structure as intermediate (if not folded)" },
        { "-euf",     FALSE, etREAL, {&Euf},
          "Initial guess for energy of activation for folding (kJ/mol)" },
        { "-efu",     FALSE, etREAL, {&Efu},
          "Initial guess for energy of activation for unfolding (kJ/mol)" },
        { "-ei",      FALSE, etREAL, {&Ei},
          "Initial guess for energy of activation for intermediates (kJ/mol)" },
        { "-maxiter", FALSE, etINT, {&maxiter},
          "Max number of iterations" },
        { "-back",    FALSE, etBOOL, {&bBack},
          "Take the back reaction into account" },
        { "-tol",     FALSE, etREAL, {&tol},
          "Absolute tolerance for convergence of the Nelder and Mead simplex algorithm" },
        { "-skip",    FALSE, etINT, {&skip},
          "Skip points in the output [TT].xvg[tt] file" },
        { "-split",   FALSE, etBOOL, {&bSplit},
          "Estimate error by splitting the number of replicas in two and refitting" },
        { "-sum",     FALSE, etBOOL, {&bSum},
          "Average folding before computing [GRK]chi[grk]^2" },
        { "-discrete", FALSE, etBOOL, {&bDiscrete},
          "Use a discrete folding criterion (F <-> U) or a continuous one" },
        { "-mult",    FALSE, etINT, {&nmult},
          "Factor to multiply the data with before discretization" }
    };
#define NPA asize(pa)

    FILE        *fp;
    real         dt_t, dt_d, dt_d2;
    int          nset_t, nset_d, nset_d2, n_t, n_d, n_d2, i;
    const char  *tfile, *dfile, *dfile2;
    t_remd_data  remd;
    output_env_t oenv;

    t_filenm     fnm[] = {
        { efXVG, "-f",    "temp",    ffREAD   },
        { efXVG, "-d",    "data",    ffREAD   },
        { efXVG, "-d2",   "data2",   ffOPTRD  },
        { efXVG, "-o",    "ft_all",  ffWRITE  },
        { efXVG, "-o2",   "it_all",  ffOPTWR  },
        { efXVG, "-o3",   "ft_repl", ffOPTWR  },
        { efXVG, "-ee",   "err_est", ffOPTWR  },
        { efLOG, "-g",    "remd",    ffWRITE  },
        { efXVG, "-m",    "melt",    ffWRITE  }
    };
#define NFILE asize(fnm)

    CopyRight(stderr, argv[0]);
    parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_BE_NICE | PCA_TIME_UNIT,
                      NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv);

#ifdef HAVE_LIBGSL
    please_cite(stdout, "Spoel2006d");
    if (cutoff < 0)
    {
        gmx_fatal(FARGS, "cutoff should be >= 0 (rather than %f)", cutoff);
    }

    tfile   = opt2fn("-f", NFILE, fnm);
    dfile   = opt2fn("-d", NFILE, fnm);
    dfile2  = opt2fn_null("-d2", NFILE, fnm);

    fp = ffopen(opt2fn("-g", NFILE, fnm), "w");

    remd.temp = read_xvg_time(tfile, bHaveT,
                              opt2parg_bSet("-b", NPA, pa), tb,
                              opt2parg_bSet("-e", NPA, pa), te,
                              nreplica, &nset_t, &n_t, &dt_t, &remd.time);
    printf("Read %d sets of %d points in %s, dt = %g\n\n", nset_t, n_t, tfile, dt_t);
    sfree(remd.time);

    remd.data = read_xvg_time(dfile, bHaveT,
                              opt2parg_bSet("-b", NPA, pa), tb,
                              opt2parg_bSet("-e", NPA, pa), te,
                              nreplica, &nset_d, &n_d, &dt_d, &remd.time);
    printf("Read %d sets of %d points in %s, dt = %g\n\n", nset_d, n_d, dfile, dt_d);

    if ((nset_t != nset_d) || (n_t != n_d) || (dt_t != dt_d))
    {
        gmx_fatal(FARGS, "Files %s and %s are inconsistent. Check log file",
                  tfile, dfile);
    }

    if (dfile2)
    {
        remd.data2 = read_xvg_time(dfile2, bHaveT,
                                   opt2parg_bSet("-b", NPA, pa), tb,
                                   opt2parg_bSet("-e", NPA, pa), te,
                                   nreplica, &nset_d2, &n_d2, &dt_d2, &remd.time);
        printf("Read %d sets of %d points in %s, dt = %g\n\n",
               nset_d2, n_d2, dfile2, dt_d2);
        if ((nset_d2 != nset_d) || (n_d != n_d2) || (dt_d != dt_d2))
        {
            gmx_fatal(FARGS, "Files %s and %s are inconsistent. Check log file",
                      dfile, dfile2);
        }
    }
    else
    {
        remd.data2 = NULL;
    }

    remd.nreplica  = nset_d;
    remd.nframe    = n_d;
    remd.dt        = 1;
    preprocess_remd(fp, &remd, cutoff, tref, ucut, bBack, Euf, Efu, Ei, t0, t1,
                    bSum, bDiscrete, nmult);

    optimize_remd_parameters(fp, &remd, maxiter, tol);

    dump_remd_parameters(fp, &remd, opt2fn("-o", NFILE, fnm),
                         opt2fn_null("-o2", NFILE, fnm),
                         opt2fn_null("-o3", NFILE, fnm),
                         opt2fn_null("-ee", NFILE, fnm),
                         opt2fn("-m", NFILE, fnm), skip, tref, oenv);

    if (bSplit)
    {
        printf("Splitting set of replicas in two halves\n");
        for (i = 0; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = FALSE;
        }
        remd.nmask = 0;
        for (i = 0; (i < remd.nreplica); i += 2)
        {
            remd.bMask[i] = TRUE;
            remd.nmask++;
        }
        sum_ft(&remd);
        optimize_remd_parameters(fp, &remd, maxiter, tol);
        dump_remd_parameters(fp, &remd, "test1.xvg", NULL, NULL, NULL, NULL, skip, tref, oenv);

        for (i = 0; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = !remd.bMask[i];
        }
        remd.nmask = remd.nreplica - remd.nmask;

        sum_ft(&remd);
        optimize_remd_parameters(fp, &remd, maxiter, tol);
        dump_remd_parameters(fp, &remd, "test2.xvg", NULL, NULL, NULL, NULL, skip, tref, oenv);

        for (i = 0; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = FALSE;
        }
        remd.nmask = 0;
        for (i = 0; (i < remd.nreplica/2); i++)
        {
            remd.bMask[i] = TRUE;
            remd.nmask++;
        }
        sum_ft(&remd);
        optimize_remd_parameters(fp, &remd, maxiter, tol);
        dump_remd_parameters(fp, &remd, "test1.xvg", NULL, NULL, NULL, NULL, skip, tref, oenv);

        for (i = 0; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = FALSE;
        }
        remd.nmask = 0;
        for (i = remd.nreplica/2; (i < remd.nreplica); i++)
        {
            remd.bMask[i] = TRUE;
            remd.nmask++;
        }
        sum_ft(&remd);
        optimize_remd_parameters(fp, &remd, maxiter, tol);
        dump_remd_parameters(fp, &remd, "test1.xvg", NULL, NULL, NULL, NULL, skip, tref, oenv);
    }
    ffclose(fp);

    view_all(oenv, NFILE, fnm);

    thanx(stderr);
#else
    fprintf(stderr, "This program should be compiled with the GNU scientific library. Please install the library and reinstall GROMACS.\n");
#endif /*HAVE_LIBGSL*/

    return 0;
}


/*
 * 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 avl_6c9ab2d0_f8bc_4fb1_bc21_7fc0c6eb4bec_h
#define avl_6c9ab2d0_f8bc_4fb1_bc21_7fc0c6eb4bec_h

#include <assert.h>
#include <gslib/std.h>

__gslib_begin__

struct _avltree_trait_copy {};
struct _avltree_trait_detach {};

template<class _ty>
struct _avltreenode_cpy_wrapper
{
    typedef _ty value;
    typedef _avltreenode_cpy_wrapper<_ty> myref;
    typedef _avltree_trait_copy tsf_behavior;

    value               _value;
    myref*              _left;
    myref*              _right;
    myref*              _parent;
    int                 _balance;

    myref()
    {
        _left = _right = _parent = nullptr;
        _balance = 0;
    }
    value* get_ptr() { return &_value; }
    const value* const_ptr() const { return &_value; }
    value& get_ref() { return _value; }
    const value& const_ref() const { return _value; }
    void born() {}
    void kill() {}
    template<class _ctor>
    void born() {}
    template<class _ctor>
    void kill() {}
    void copy(const myref* a) { get_ref() = a->const_ref(); }
    void attach(myref* a) { assert(0); }
    void swap_data(myref* a) { std::swap(_value, a->_value); }
};

template<class _ty>
struct _avltreenode_wrapper
{
    typedef _ty value;
    typedef _avltreenode_wrapper<_ty> myref;
    typedef _avltree_trait_detach tsf_behavior;

    value*              _value;
    myref*              _left;
    myref*              _right;
    myref*              _parent;
    int                 _balance;

    myref()
    {
        _left = _right = _parent = nullptr;
        _value = nullptr;
        _balance = 0;
    }
    value* get_ptr() { return _value; }
    const value* const_ptr() const { return _value; }
    value& get_ref() { return *_value; }
    const value& const_ref() const { return *_value; }
    void copy(const myref* a) { get_ref() = a->const_ref(); }
    void born() { !! }
    template<class _ctor>
    void born() { _value = new _ctor; }
    void kill() { if(_value) { delete _value; _value = nullptr; } }
    template<class _ctor>
    void kill() { if(_value) { delete _value; _value = nullptr; } }
    void attach(myref* a)
    {
        assert(a && a->_value);
        kill();
        _value = a->_value;
        a->_value = nullptr;
    }
    void swap_data(myref* a) { gs_swap(_value, a->_value); }
};

template<class _wrapper>
struct _avltree_allocator
{
    typedef _wrapper wrapper;
    static wrapper* born() { return new wrapper; }
    static void kill(wrapper* w) { delete w; }
};

template<class _val>
struct _avltreenode_val
{
    typedef _val value;
    typedef const _val const_value;
    typedef _avltreenode_val<_val> myref;

    union
    {
        value*          _vptr;
        const_value*    _cvptr;
    };

    myref() { _vptr = nullptr; }
    value* get_wrapper() const { return _vptr; }
    operator bool() const { return _vptr != nullptr; }
    bool is_left() const { return (_cvptr && _cvptr->_parent) ? _cvptr->_parent->_left == _cvptr : false; }
    bool is_right() const { return (_cvptr && _cvptr->_parent) ? _cvptr->_parent->_right == _cvptr : false; }
    bool is_root() const { return _cvptr ? (!_cvptr->_parent) : false; }
    bool is_leaf() const { return _cvptr ? (!_cvptr->_left && !_cvptr->_right) : false; }
    int up_depth() const
    {
        int depth = 0;
        for(value* p = _vptr; p; p = p->_parent, depth ++);
        return depth;
    }
    int down_depth() const { return _down_depth(_vptr, 0); }
    bool operator==(const value* v) const { return _vptr == v; }
    bool operator!=(const value* v) const { return _vptr != v; }
    int get_balance() const { return _vptr->_balance; }
    void set_balance(int b) { _vptr->_balance = b; }
    void swap_data(myref& a) { _vptr->swap_data(a._vptr); }

public:
    static void connect_left_child(value* p, value* l)
    {
        assert(p);
        p->_left = l;
        if(l)
            l->_parent = p;
    }
    static void connect_right_child(value* p, value* r)
    {
        assert(p);
        p->_right = r;
        if(r)
            r->_parent = p;
    }
    static bool disconnect_parent_child(value* p, value* c)
    {
        assert(p && c && (c->_parent == p));
        if(p->_left == c) {
            p->_left = c->_parent = nullptr;
            return true;
        }
        assert(p->_right == c);
        p->_right = c->_parent = nullptr;
        return false;
    }

private:
    static int _down_depth(value* v, int ctr)
    {
        if(v == nullptr)
            return ctr;
        ctr ++;
        return gs_max(_down_depth(v->_left, ctr),
            _down_depth(v->_right, ctr)
            );
    }

protected:
    value* vleft() const { return _vptr ? _vptr->_left : nullptr; }
    value* vright() const { return _vptr ? _vptr->_right : nullptr; }
    value* vparent() const { return _vptr ? _vptr->_parent : nullptr; }
    value* vsibling() const
    {
        if(!_vptr || !_vptr->_parent)
            return nullptr;
        if(is_left())
            return _vptr->_right;
        else if(is_right())
            return _vptr->_left;
        assert(!"unexpected.");
        return nullptr;
    }
    value* vroot() const
    {
        if(!_vptr)
            return nullptr;
        value* p = _vptr;
        for( ; p->_parent; p = p->_parent);
        return p;
    }

protected:
    template<class _lambda, class _value>
    static void preorder_traversal(_lambda lam, _value* v)
    {
        assert(v);
        lam(v);
        if(v->_left)
            preorder_traversal(lam, v->_left);
        if(v->_right)
            preorder_traversal(lam, v->_right);
    }
    template<class _lambda, class _value>
    static void inorder_traversal(_lambda lam, _value* v)
    {
        assert(v);
        if(v->_left)
            inorder_traversal(lam, v->_left);
        lam(v);
        if(v->_right)
            inorder_traversal(lam, v->_right);
    }
    template<class _lambda, class _value>
    static void postorder_traversal(_lambda lam, _value* v)
    {
        assert(v);
        if(v->_left)
            postorder_traversal(lam, v->_left);
        if(v->_right)
            postorder_traversal(lam, v->-right);
        lam(v);
    }

public:
    template<class _lambda>
    void inorder_traversal(_lambda lam) { if(_vptr) inorder_traversal(lam, _vptr); }
    template<class _lambda>
    void inorder_traversal(_lambda lam) const { if(_cvptr) inorder_traversal(lam, _cvptr); }
    template<class _lambda>
    void preorder_traversal(_lambda lam) { if(_vptr) preorder_traversal(lam, _vptr); }
    template<class _lambda>
    void preorder_traversal(_lambda lam) const { if(_cvptr) preorder_traversal(lam, _cvptr); }
    template<class _lambda>
    void postorder_traversal(_lambda lam) { if(_vptr) postorder_traversal(lam, _vptr); }
    template<class _lambda>
    void postorder_traversal(_lambda lam) const { if(_cvptr) postorder_traversal(lam, _cvptr); }
};

template<class _ty,
    class _wrapper = _avltreenode_cpy_wrapper<_ty> >
class _avltree_const_iterator:
    public _avltreenode_val<_wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _avltree_const_iterator<_ty, _wrapper> iterator;

public:
    iterator(const wrapper* w = nullptr) { _cvptr = w; }
    bool is_valid() const { return _cvptr != nullptr; }
    const value* get_ptr() const { return _cvptr->const_ptr(); }
    const value* operator->() const { return _cvptr->const_ptr(); }
    const value& operator*() const { return _cvptr->const_ref(); }
    iterator left() const { return iterator(vleft()); }
    iterator right() const { return iterator(vright()); }
    iterator parent() const { return iterator(vparent()); }
    iterator sibling() const { return iterator(vsibling()); }
    iterator root() const { return iterator(vroot()); }
    bool operator==(const iterator& that) const { return _cvptr == that._cvptr; }
    bool operator!=(const iterator& that) const { return _cvptr != that._cvptr; }
};

template<class _ty,
    class _wrapper = _avltreenode_cpy_wrapper<_ty> >
class _avltree_iterator:
    public _avltree_const_iterator<_ty, _wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _avltree_const_iterator<_ty, _wrapper> const_iterator;
    typedef _avltree_const_iterator<_ty, _wrapper> superref;
    typedef _avltree_iterator<_ty, _wrapper> iterator;

public:
    iterator(wrapper* w): superref(w) {}
    value* get_ptr() const { return _vptr->get_ptr(); }
    value* operator->() const { return _vptr->get_ptr(); }
    value& operator*() const { return _vptr->get_ref(); }
    bool operator==(const iterator& that) const { return _vptr == that._vptr; }
    bool operator!=(const iterator& that) const { return _vptr != that._vptr; }
    bool operator==(const const_iterator& that) const { return _vptr == that._vptr; }
    bool operator!=(const const_iterator& that) const { return _vptr != that._vptr; }
    operator const_iterator() { return const_iterator(_cvptr); }
    void to_root() { _vptr = vroot(); }
    void to_left() { _vptr = vleft(); }
    void to_right() { _vptr = vright(); }
    void to_sibling() { _vptr = vsibling(); }
    void to_parent() { _vptr = vparent(); }
    iterator left() const { return iterator(vleft()); }
    iterator right() const { return iterator(vright()); }
    iterator parent() const { return iterator(vparent()); }
    iterator sibling() const { return iterator(vsibling()); }
    iterator root() const { return iterator(vroot()); }
};

template<class _ty,
    class _wrapper = _avltreenode_cpy_wrapper<_ty>,
    class _alloc = _avltree_allocator<_wrapper> >
class avltree:
    public _avltreenode_val<_wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _alloc alloc;
    typedef avltree<value, wrapper, alloc> myref;
    typedef _avltree_const_iterator<_ty, _wrapper> const_iterator;
    typedef _avltree_iterator<_ty, _wrapper> iterator;

public:
    avltree() { _vptr = nullptr; }
    ~avltree() { clear(); }
    void clear() { destroy(get_root()); }
    void destroy(iterator i)
    {
        if(!i.is_valid())
            return;
        if(iterator p = i.parent())
            disconnect_parent_child(p.get_wrapper(), i.get_wrapper());
        else {
            assert(is_root(i));
            _vptr = nullptr;
        }
        _destroy(i);
    }
    void adopt(wrapper* w)
    {
        assert(!_vptr && "use attach method.");
        _vptr = w;
    }
    iterator get_root() const { return iterator(_vptr); }
    const_iterator const_root() const { return const_iterator(_cvptr); }
    bool is_root(iterator i) const { return i.is_valid() ? (_cvptr == i.get_wrapper()) : false; }
    bool is_valid() const { return _cvptr != nullptr; }
    bool is_mine(iterator i) const
    {
        if(!i.is_valid())
            return false;
        i.to_root();
        return i.get_wrapper() == _vptr;
    }
    int depth() const { return _cvptr->down_depth(); }
    void swap(myref& that) { gs_swap(_vptr, that._vptr); }
    iterator find(const value& v) const { return find(get_root(), v); }
    iterator find(iterator i, const value& v) const
    {
        if(!i) {
            if(!is_valid())
                return i;
            i = get_root();
        }
        assert(i);
        if(v == *i)
            return i;
        iterator n = (v < *i) ? i.left() : i.right();
        return n ? find(n, v) : i;
    }
    template<class _ctor = value>
    iterator insert(const value& v)
    {
        iterator i = get_root();
        return !i ? _init<_ctor>(v) : _insert<_ctor>(i, v);
    }
    void erase(iterator i)
    {
        assert(i && is_mine(i));
        if(i.is_leaf()) {
            iterator p = i.parent();
            if(p) {
                disconnect_parent_child(p.get_wrapper(), i.get_wrapper());
                _destroy(i);
                _balance_erase(p);
                return;
            }
            else {
                assert(is_root(i));
                _destroy(i);
                _vptr = nullptr;
                return;
            }
        }
        if(i.left()) {
            if(i.right()) {     /* left & right */
                iterator l = i.left();
                iterator li = l;
                for(; li.right(); li.to_right());
                iterator lil = li.left();
                i.swap_data(li);
                if(lil) {
                    li.swap_data(lil);
                    destroy(lil);
                    _balance_erase(li);
                    return;
                }
                else {
                    iterator q = li.parent();
                    assert(q);
                    destroy(li);
                    _balance_erase(q);
                    return;
                }
            }
            else {      /* only left */
                if(i.parent()) {
                    myref t;
                    iterator j = attach(detach(t, i.left()), i);
                    j.set_balance(i.get_balance());
                    _balance_erase(j);
                    return;
                }
                else {
                    myref t;
                    swap(detach(t, i.left()));
                    return;
                }
            }
        }
        else {
            assert(i.right());      /* only right */
            if(i.parent()) {
                myref t;
                iterator j = attach(detach(t, i.right()), i);
                j.set_balance(i.get_balance());
                _balance_erase(j);
                return;
            }
            else {
                myref t;
                swap(detach(t, i.right()));
                return;
            }
        }
    }
    void erase(const value& v)
    {
        if(iterator i = find(v))
            erase(i);
    }

    /* The detach and attach methods, provide subtree operations */
    myref& detach(myref& subtree, iterator i)
    {
        assert(i && is_mine(i));
        if(subtree.is_valid())
            subtree.clear();
        detach<myref>(subtree, i);
        return subtree;
    }
    template<class _cont>
    void detach(_cont& cont)
    {
        cont.adopt(_vptr);
        _vptr = nullptr;
    }
    template<class _cont>
    void detach(_cont& cont, iterator i)
    {
        assert(i && is_mine(i));
        if(i == get_root())
            return detach(cont);
        iterator p = i.parent();
        assert(p);
        disconnect_parent_child(p.get_wrapper(), i.get_wrapper());
        cont.adopt(i.get_wrapper());
    }
    iterator attach(myref& subtree, iterator i)
    {
        assert(i && is_mine(i) && i.is_leaf());
        if(i.is_root()) {
            swap(subtree);
            return get_root();
        }
        iterator p = i.parent();
        assert(p);
        bool leftp = disconnect_parent_child(p.get_wrapper(), i.get_wrapper());
        gs_swap(subtree._vptr, i._vptr);
        leftp ? connect_left_child(p.get_wrapper(), i.get_wrapper()) :
            connect_right_child(p.get_wrapper(), i.get_wrapper());
        subtree.clear();
        return i;
    }

public:
    template<class _lambda>
    void preorder_for_each(_lambda lam) { preorder_traversal([](wrapper* w) { lam(w->get_ptr()); }); }
    template<class _lambda>
    void preorder_const_for_each(_lambda lam) const { preorder_traversal([](const wrapper* w) { lam(w->const_ptr()); }); }
    template<class _lambda>
    void inorder_for_each(_lambda lam) { inorder_traversal([](wrapper* w) { lam(w->get_ptr()); }); }
    template<class _lambda>
    void inorder_const_for_each(_lambda lam) const { inorder_traversal([](const wrapper* w) { lam(w->const_ptr()); }); }
    template<class _lambda>
    void postorder_for_each(_lambda lam) { postorder_traversal([](wrapper* w) { lam(w->get_ptr()); }); }
    template<class _lambda>
    void postorder_const_for_each(_lambda lam) const { postorder_traversal([](const wrapper* w) { lam(w->const_ptr()); }); }

protected:
    void _destroy(iterator i)
    {
        if(!i.is_valid())
            return;
        _destroy(i.left());
        _destroy(i.right());
        wrapper* w = i.get_wrapper();
        w->kill();
        alloc::kill(w);
    }
    template<class _ctor>
    iterator _insert(iterator i, const value& v)
    {
        assert(i);
        if(v == *i)
            return iterator(nullptr);     /* failed */
        if(v < *i) {
            if(i.left())
                return _insert<_ctor>(i.left(), v);
            iterator j = _add_left<_ctor>(i, v);
            _balance_insert(j);
            return j;
        }
        else {
            if(i.right())
                return _insert<_ctor>(i.right(), v);
            iterator j = _add_right<_ctor>(i, v);
            _balance_insert(j);
            return j;
        }
    }
    void _balance_insert(iterator i)
    {
        assert(i);
        iterator p = i.parent();
        if(!p)
            return;
        if(i.is_left()) {
            switch(p.get_balance())
            {
            case -1:
                (i.get_balance() == 1) ?
                    _left_right_rotate(p) :
                    _right_rotate(p);
                break;
            case 0:
                p.set_balance(-1);
                return _balance_insert(p);
            case 1:
                p.set_balance(0);
                break;
            default:
                assert(!"unexpected.");
                break;
            }
        }
        else {
            switch(p.get_balance())
            {
            case -1:
                p.set_balance(0);
                break;
            case 0:
                p.set_balance(1);
                return _balance_insert(p);
            case 1:
                (i.get_balance() == -1) ?
                    _right_left_rotate(p) :
                    _left_rotate(p);
                break;
            default:
                assert(!"unexpected.");
                break;
            }
        }
    }
    void _balance_erase(iterator i)
    {
        assert(i);
        int b = i.get_balance();
        if(!b) {
            i.set_balance(i.left() ? -1 : 1);
            return;
        }
        if(!i.left()) {
            if(b == -1)
                i.set_balance(0);
            else if(b == 1) {
                iterator r = i.right();
                if(!r)
                    i.set_balance(0);
                else {
                    (r.get_balance() == -1) ?
                        _right_left_rotate(i) :
                        _left_rotate(i);
                    i.to_parent();
                    if(i.get_balance() == -1)
                        return;
                }
            }
            else {
                assert(!"unexpected.");
            }
        }
        else if(!i.right()) {
            if(b == 1)
                i.set_balance(0);
            else if(b == -1) {
                iterator l = i.left();
                if(!l)
                    i.set_balance(0);
                else {
                    (l.get_balance() == 1) ?
                        _left_right_rotate(i) :
                        _right_rotate(i);
                    i.to_parent();
                    if(i.get_balance() == 1)
                        return;
                }
            }
            else {
                assert(!"unexpected.");
            }
        }
        _balance_erase_(i);
    }
    void _balance_erase_(iterator i)
    {
        assert(i);
        iterator p = i.parent();
        if(!p)
            return;
        if(i.is_left()) {
            switch(p.get_balance())
            {
            case -1:
                p.set_balance(0);
                return _balance_erase_(p);
            case 0:
                p.set_balance(1);
                break;
            case 1:
                (p.right().get_balance() == -1) ?
                    _right_left_rotate(p) :
                    _left_rotate(p);
                if(p.parent().get_balance() != -1)
                    return _balance_erase_(p.parent());
                break;
            default:
                assert(!"unexpected.");
                break;
            }
        }
        else {
            switch(p.get_balance())
            {
            case -1:
                (p.left().get_balance() == 1) ?
                    _left_right_rotate(p) :
                    _right_rotate(p);
                if(p.parent().get_balance() != 1)
                    return _balance_erase_(p.parent());
                break;
            case 0:
                p.set_balance(-1);
                break;
            case 1:
                p.set_balance(0);
                return _balance_erase_(p);
            default:
                assert(!"unexpected.");
                break;
            }
        }
    }
    iterator _left_rotate(iterator i)
    {
        assert(i && i.right());
        iterator p = i.parent();
        iterator r = i.right();
        iterator rl = r.left();
        if(p) {
            i.is_left() ? connect_left_child(p.get_wrapper(), r.get_wrapper()) :
                connect_right_child(p.get_wrapper(), r.get_wrapper());
        }
        else {
            _vptr = r.get_wrapper();
            _vptr->_parent = nullptr;
        }
        connect_left_child(r.get_wrapper(), i.get_wrapper());
        connect_right_child(i.get_wrapper(), rl.get_wrapper());
        if(r.get_balance() == 0) {
            i.set_balance(1);
            r.set_balance(-1);
        }
        else {
            i.set_balance(0);
            r.set_balance(0);
        }
        return r;
    }
    iterator _right_rotate(iterator i)
    {
        assert(i && i.left());
        iterator p = i.parent();
        iterator l = i.left();
        iterator lr = l.right();
        if(p) {
            i.is_left() ? connect_left_child(p.get_wrapper(), l.get_wrapper()) :
                connect_right_child(p.get_wrapper(), l.get_wrapper());
        }
        else {
            _vptr = l.get_wrapper();
            _vptr->_parent = nullptr;
        }
        connect_right_child(l.get_wrapper(), i.get_wrapper());
        connect_left_child(i.get_wrapper(), lr.get_wrapper());
        if(l.get_balance() == 0) {
            i.set_balance(-1);
            l.set_balance(1);
        }
        else {
            i.set_balance(0);
            l.set_balance(0);
        }
        return l;
    }
    iterator _left_right_rotate(iterator i)
    {
        assert(i && i.left());
        iterator l = i.left();
        iterator lr = l.right();
        assert(lr);
        iterator lrl = lr.left();
        iterator lrr = lr.right();
        iterator p = i.parent();
        if(p) {
            i.is_left() ? connect_left_child(p.get_wrapper(), lr.get_wrapper()) :
                connect_right_child(p.get_wrapper(), lr.get_wrapper());
        }
        else {
            _vptr = lr.get_wrapper();
            _vptr->_parent = nullptr;
        }
        connect_left_child(lr.get_wrapper(), l.get_wrapper());
        connect_right_child(lr.get_wrapper(), i.get_wrapper());
        connect_right_child(l.get_wrapper(), lrl.get_wrapper());
        connect_left_child(i.get_wrapper(), lrr.get_wrapper());
        switch(lr.get_balance())
        {
        case -1:
            i.set_balance(1);
            l.set_balance(0);
            break;
        case 0:
            i.set_balance(0);
            l.set_balance(0);
            break;
        case 1:
            i.set_balance(0);
            l.set_balance(-1);
            break;
        default:
            assert(!"unexpected.");
            break;
        }
        lr.set_balance(0);
        return lr;
    }
    iterator _right_left_rotate(iterator i)
    {
        assert(i && i.right());
        iterator r = i.right();
        iterator rl = r.left();
        assert(rl);
        iterator rll = rl.left();
        iterator rlr = rl.right();
        iterator p = i.parent();
        if(p) {
            i.is_left() ? connect_left_child(p.get_wrapper(), rl.get_wrapper()) :
                connect_right_child(p.get_wrapper(), rl.get_wrapper());
        }
        else {
            _vptr = rl.get_wrapper();
            _vptr->_parent = nullptr;
        }
        connect_right_child(rl.get_wrapper(), r.get_wrapper());
        connect_left_child(rl.get_wrapper(), i.get_wrapper());
        connect_right_child(i.get_wrapper(), rll.get_wrapper());
        connect_left_child(r.get_wrapper(), rlr.get_wrapper());
        switch(rl.get_balance())
        {
        case -1:
            i.set_balance(0);
            r.set_balance(1);
            break;
        case 0:
            i.set_balance(0);
            r.set_balance(0);
            break;
        case 1:
            i.set_balance(-1);
            r.set_balance(0);
            break;
        default:
            assert(!"unexpected.");
            break;
        }
        rl.set_balance(0);
        return rl;
    }

    template<class _ctor>
    iterator _init()
    {
        assert(!_vptr);
        _vptr = alloc::born();
        _vptr->born<_ctor>();
        return iterator(_vptr);
    }
    template<class _ctor>
    iterator _init(const value& v)
    {
        assert(!_vptr);
        _vptr = initval<_ctor, wrapper::tsf_behavior>::run(alloc::born(), v);
        assert(_vptr);
        return iterator(_vptr);
    }
    template<class _ctor>
    iterator _add_left(iterator i, const value& v)
    {
        assert(i && !i.left());
        wrapper* n = initval<_ctor, wrapper::tsf_behavior>::run(alloc::born(), v);
        connect_left_child(i.get_wrapper(), n);
        return iterator(n);
    }
    template<class _ctor>
    iterator _add_right(iterator i, const value& v)
    {
        assert(i && !i.right());
        wrapper* n = initval<_ctor, wrapper::tsf_behavior>::run(alloc::born(), v);
        connect_right_child(i.get_wrapper(), n);
        return iterator(n);
    }
    void _modified()
    {
#if defined (DEBUG) || defined (_DEBUG)
        debug_check(get_root());
#endif
    }

protected:
    friend struct initval;
    template<class _ctor, class _tsftrait>
    struct initval;
    template<class _ctor>
    struct initval<_ctor, _avltree_trait_copy>
    {
        static wrapper* run(wrapper* w, const value& v)
        {
            assert(w);
            w->get_ref() = v;
            return w;
        }
    };
    template<class _ctor>
    struct initval<_ctor, _avltree_trait_detach>
    {
        static wrapper* run(wrapper* w, const value& v)
        {
            assert(w);
            w->_value = &v;     /* or duplicate? */
            return w;
        }
    };

public:
    bool debug_check(iterator i)
    {
        if(!i)
            return true;
        check_root(i);
        check_linkage(i);
        check_order(i);
        check_balance(i);
        iterator l = i.left(), r = i.right();
        if(!(l && debug_check(l)))
            return false;
        if(!(r && debug_check(r)))
            return false;
        return true;
    }
    bool check_root(iterator i)
    {
        assert(i);
        if(is_root(i)) {
            assert(!i.parent() && "root has no parent.");
            return true;
        }
        assert(i.parent() && "non-root must have parent.");
        return false;
    }
    void check_linkage(iterator i)
    {
        assert(i);
        iterator l = i.left(), r = i.right();
        if(l) { assert(l.parent() == i && "left link wrong."); }
        if(r) { assert(r.parent() == i && "right link wrong."); }
    }
    void check_order(iterator i)
    {
        assert(i);
        iterator l = i.left(), r = i.right();
        if(l) {
            iterator lm = l;
            for(; lm.right(); lm = lm.right());
            assert(*lm < *i && "left order wrong.");
        }
        if(r) {
            iterator rm = r;
            for(; rm.left(); rm = rm.left());
            assert(*i < *rm && "right order wrong.");
        }
    }
    void check_balance(iterator i)
    {
        assert(i);
        iterator l = i.left(), r = i.right();
        int h1 = 0, h2 = 0;
        if(l) h1 = l.down_depth();
        if(r) h2 = r.down_depth();
        int b = i.get_balance();
        assert(b == h2 - h1 && "balance wrong.");
    }
};

__gslib_end__

#endif


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

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

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

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

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gsl/gsl_sort_float.h>
#include <gsl/gsl_statistics_float.h>
#include <gsl/gsl_integration.h>
#include "psrsalsa.h"
int filterPApoints(datafile_definition *datafile, verbose_definition verbose)
{
  int dPa_polnr;
  long i, j, nrpoints;
  float *olddata;
  if(datafile->poltype != POLTYPE_ILVPAdPA && datafile->poltype != POLTYPE_PAdPA && datafile->poltype != POLTYPE_ILVPAdPATEldEl) {
    printerror(verbose.debug, "ERROR filterPApoints: Data doesn't appear to have poltype ILVPAdPA, PAdPA or ILVPAdPATEldEl.");
    return 0;
  }
  if(datafile->poltype == POLTYPE_ILVPAdPA && datafile->NrPols != 5) {
    printerror(verbose.debug, "ERROR filterPApoints: 5 polarization channels were expected, but there are only %ld.", datafile->NrPols);
    return 0;
  }else if(datafile->poltype == POLTYPE_ILVPAdPATEldEl && datafile->NrPols != 8) {
    printerror(verbose.debug, "ERROR filterPApoints: 8 polarization channels were expected, but there are only %ld.", datafile->NrPols);
    return 0;
  }else if(datafile->poltype == POLTYPE_PAdPA && datafile->NrPols != 2) {
    printerror(verbose.debug, "ERROR filterPApoints: 2 polarization channels were expected, but there are only %ld.", datafile->NrPols);
    return 0;
  }
  if(datafile->NrSubints > 1 || datafile->NrFreqChan > 1) {
    printerror(verbose.debug, "ERROR filterPApoints: Can only do this opperation if there is one subint and one frequency channel.");
    return 0;
  }
  if(datafile->tsampMode != TSAMPMODE_LONGITUDELIST) {
    printerror(verbose.debug, "ERROR filterPApoints: Expected pulse longitudes to be defined.");
    return 0;
  }
  if(datafile->poltype == POLTYPE_ILVPAdPA || datafile->poltype == POLTYPE_ILVPAdPATEldEl) {
    dPa_polnr = 4;
  }else if(datafile->poltype == POLTYPE_PAdPA) {
    dPa_polnr = 1;
  }
  nrpoints = 0;
  for(i = 0; i < datafile->NrBins; i++) {
    if(datafile->data[i+dPa_polnr*datafile->NrBins] > 0) {
      nrpoints++;
    }
  }
  if(verbose.verbose)
    printf("Keeping %ld significant PA points\n", nrpoints);
  olddata = datafile->data;
  datafile->data = (float *)malloc(nrpoints*datafile->NrPols*sizeof(float));
  if(datafile->data == NULL) {
    printerror(verbose.debug, "ERROR filterPApoints: Memory allocation error.");
    return 0;
  }
  j = 0;
  for(i = 0; i < datafile->NrBins; i++) {
    if(olddata[i+dPa_polnr*datafile->NrBins] > 0) {
      if(datafile->poltype == POLTYPE_ILVPAdPA) {
 datafile->data[j+0*nrpoints] = olddata[i+0*datafile->NrBins];
 datafile->data[j+1*nrpoints] = olddata[i+1*datafile->NrBins];
 datafile->data[j+2*nrpoints] = olddata[i+2*datafile->NrBins];
 datafile->data[j+3*nrpoints] = olddata[i+3*datafile->NrBins];
 datafile->data[j+4*nrpoints] = olddata[i+4*datafile->NrBins];
      }else if(datafile->poltype == POLTYPE_ILVPAdPATEldEl) {
 datafile->data[j+0*nrpoints] = olddata[i+0*datafile->NrBins];
 datafile->data[j+1*nrpoints] = olddata[i+1*datafile->NrBins];
 datafile->data[j+2*nrpoints] = olddata[i+2*datafile->NrBins];
 datafile->data[j+3*nrpoints] = olddata[i+3*datafile->NrBins];
 datafile->data[j+4*nrpoints] = olddata[i+4*datafile->NrBins];
 datafile->data[j+5*nrpoints] = olddata[i+5*datafile->NrBins];
 datafile->data[j+6*nrpoints] = olddata[i+6*datafile->NrBins];
 datafile->data[j+7*nrpoints] = olddata[i+7*datafile->NrBins];
      }else {
 datafile->data[j+0*nrpoints] = olddata[i+0*datafile->NrBins];
 datafile->data[j+1*nrpoints] = olddata[i+1*datafile->NrBins];
      }
      datafile->tsamp_list[j] = datafile->tsamp_list[i];
      j++;
    }
  }
  free(olddata);
  datafile->NrBins = nrpoints;
  return datafile->NrBins;
}
int make_paswing_fromIQUV(datafile_definition *datafile, int extended, pulselongitude_regions_definition onpulse, int normalize, int correctLbias, float correctQV, float correctV, int nolongitudes, float loffset, float paoffset, datafile_definition *rms_file, float rebin_factor, verbose_definition verbose)
{
  int indent, rms_file_specified;
  long i, j, NrOffpulseBins, pulsenr, freqnr, output_nr_pols;
  float ymax, baseline_intensity, RMSQ, RMSU, *Loffpulse, *Poffpulse, medianL, medianP, *newdata, *newdata_rms;
  rms_file_specified = 1;
  if(rms_file == NULL) {
    rms_file = datafile;
    rms_file_specified = 0;
  }
  if(verbose.verbose) {
    for(indent = 0; indent < verbose.indent; indent++) printf(" ");
    printf("Constructing PA and degree of linear polarization");
    if(extended)
      printf(", total polarization and ellipticity");
    if(rms_file_specified)
      printf(" (using a seperate file to determine the off-pulse rms)");
    printf("\n");
    for(indent = 0; indent < verbose.indent; indent++) printf(" ");
    printf("  Reference frequency for PA is ");
    if(datafile->isDeFarad) {
      if((datafile->freq_ref > -1.1 && datafile->freq_ref < -0.9) || (datafile->freq_ref > 0.99e10 && datafile->freq_ref < 1.01e10))
 printf("infinity\n");
      else if(datafile->freq_ref < 0)
 printf("unknown\n");
      else
 printf("%f MHz\n", datafile->freq_ref);
    }else {
      if(datafile->NrFreqChan == 1)
 printf("%lf MHz\n", get_centre_frequency(*datafile, verbose));
      else
 printf("observing frequencies of individual frequency channels\n");
    }
    for(indent = 0; indent < verbose.indent; indent++) printf(" ");
    printf("  ");
    switch(correctLbias) {
    case -1: printf("No L de-bias applied"); break;
    case 0: printf("De-bias L using median noise subtraction"); break;
    case 1: printf("De-bias L using Wardle & Kronberg correction"); break;
    default: printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Undefined L de-bias method specified."); return 0;
    }
    if(correctQV != 1 || correctV != 1)
      printf(", Q correction factor %f, V correction factor %f", 1.0/correctQV, 1.0/(correctQV*correctV));
    if(normalize)
      printf(", output is normalised");
    if(loffset != 0)
      printf(", pulse longitude shifted by %f deg\n", loffset);
    if(paoffset != 0)
      printf(", PA shifted by %f deg\n", paoffset);
    printf("\n");
    if(extended) {
      printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Total polarization is computed, and the median off-pulse value is subtracted (probably not a very good idea).");
    }
  }
  if(datafile->NrPols != 4 || rms_file->NrPols != 4) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Expected 4 input polarizations.");
    return 0;
  }
  if(datafile->poltype != POLTYPE_STOKES) {
    if(datafile->poltype == POLTYPE_UNKNOWN) {
      printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Polarization state unknown, it is assumed the data are Stokes parameters.");
    }else {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Convert data into Stokes parameters first.");
      return 0;
    }
  }
  if(rms_file_specified) {
    if(rms_file->poltype != POLTYPE_STOKES) {
      if(rms_file->poltype == POLTYPE_UNKNOWN) {
 printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Polarization state of the data to be used to determine the off-pulse rms is unknown, it is assumed the data are Stokes parameters.");
      }else {
 printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Convert data to be used to determine the off-pulse rms into Stokes parameters first.");
 return 0;
      }
    }
  }
  if(datafile->tsampMode != TSAMPMODE_FIXEDTSAMP) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Expects input to have a regular sampling.");
    return 0;
  }
  if(correctQV == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: correctQV is set to zero, you probably want this to be 1.");
    return 0;
  }
  if(correctV == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: correctV is set to zero, you probably want this to be 1.");
    return 0;
  }
  if(datafile->isDebase == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Please remove baseline first, i.e. use pmod -debase.");
    return 0;
  }else if(datafile->isDebase != 1) {
    fflush(stdout);
    printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Unknown baseline state. It is assumed the baseline has already removed from the data.");
  }
  if(rms_file_specified) {
    if(rms_file->isDebase == 0) {
      fflush(stdout);
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Please remove baseline first, i.e. use pmod -debase.");
      return 0;
    }else if(rms_file->isDebase != 1) {
      fflush(stdout);
      printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Unknown baseline state. It is assumed the baseline has already removed from the data.");
    }
  }
  if(rms_file_specified) {
    if(datafile->NrSubints != rms_file->NrSubints) {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Number of subintegrations is different in the data to be used to determine the off-pulse rms.");
 return 0;
    }
    if(datafile->NrFreqChan != rms_file->NrFreqChan) {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Number of frequency channels is different in the data to be used to determine the off-pulse rms (%ld != %ld).", rms_file->NrFreqChan, datafile->NrFreqChan);
 return 0;
    }
    if(correctLbias == 0) {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Subtracting the median of L is not supported when a separate file is used for the offpulse statistics.");
      return 0;
    }
    if(extended) {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Subtracting the median of P is not supported when a separate file is used for the offpulse statistics.");
      return 0;
    }
  }
  if(datafile->offpulse_rms != NULL) {
    free(datafile->offpulse_rms);
  }
  Loffpulse = (float *)malloc((rms_file->NrBins)*sizeof(float));
  Poffpulse = (float *)malloc((rms_file->NrBins)*sizeof(float));
  if(extended) {
    output_nr_pols = 8;
  }else {
    output_nr_pols = 5;
  }
  newdata = (float *)malloc(datafile->NrBins*datafile->NrSubints*datafile->NrFreqChan*output_nr_pols*sizeof(float));
  if(rms_file_specified) {
    newdata_rms = (float *)malloc(rms_file->NrBins*rms_file->NrSubints*rms_file->NrFreqChan*output_nr_pols*sizeof(float));
  }else {
    newdata_rms = newdata;
  }
  datafile->offpulse_rms = (float *)malloc(datafile->NrSubints*datafile->NrFreqChan*output_nr_pols*sizeof(float));
  if(Loffpulse == NULL || Poffpulse == NULL || newdata == NULL || datafile->offpulse_rms == NULL || newdata_rms == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Memory allocation error.");
    return 0;
  }
  if(nolongitudes == 0) {
    datafile->tsamp_list = (double *)malloc(datafile->NrBins*sizeof(double));
    if(datafile->tsamp_list == NULL) {
      fflush(stdout);
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Memory allocation error.");
      return 0;
    }
    for(j = 0; j < (datafile->NrBins); j++) {
      datafile->tsamp_list[j] = get_pulse_longitude(*datafile, 0, j, verbose);
      datafile->tsamp_list[j] += loffset;
    }
  }
  if(normalize && (datafile->NrSubints > 1 || datafile->NrFreqChan > 1)) {
    fflush(stdout);
    printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Normalization will cause all subintegrations/frequency channels to be normalised individually. This may not be desired.");
  }
  long sindex_I, sindex_Q, sindex_U, sindex_V, sindex_I_rms, sindex_Q_rms, sindex_U_rms, sindex_V_rms, newindex_I, newindex_L, newindex_V, newindex_Pa, newindex_dPa, newindex_T, newindex_Ell, newindex_dEll, newindex_L_rms, newindex_T_rms;
  for(pulsenr = 0; pulsenr < datafile->NrSubints; pulsenr++) {
    for(freqnr = 0; freqnr < datafile->NrFreqChan; freqnr++) {
      sindex_I = datafile->NrBins*(0+datafile->NrPols*(freqnr+pulsenr*datafile->NrFreqChan));
      sindex_Q = datafile->NrBins*(1+datafile->NrPols*(freqnr+pulsenr*datafile->NrFreqChan));
      sindex_U = datafile->NrBins*(2+datafile->NrPols*(freqnr+pulsenr*datafile->NrFreqChan));
      sindex_V = datafile->NrBins*(3+datafile->NrPols*(freqnr+pulsenr*datafile->NrFreqChan));
      newindex_I = datafile->NrBins*(0+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      newindex_L = datafile->NrBins*(1+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      newindex_V = datafile->NrBins*(2+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      newindex_Pa = datafile->NrBins*(3+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      newindex_dPa = datafile->NrBins*(4+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      if(extended) {
 newindex_T = datafile->NrBins*(5+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
 newindex_Ell = datafile->NrBins*(6+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
 newindex_dEll = datafile->NrBins*(7+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      }
      if(rms_file_specified) {
 sindex_I_rms = rms_file->NrBins*(0+rms_file->NrPols*(freqnr+pulsenr*rms_file->NrFreqChan));
 sindex_Q_rms = rms_file->NrBins*(1+rms_file->NrPols*(freqnr+pulsenr*rms_file->NrFreqChan));
 sindex_U_rms = rms_file->NrBins*(2+rms_file->NrPols*(freqnr+pulsenr*rms_file->NrFreqChan));
 sindex_V_rms = rms_file->NrBins*(3+rms_file->NrPols*(freqnr+pulsenr*rms_file->NrFreqChan));
 newindex_L_rms = rms_file->NrBins*(1+output_nr_pols*(freqnr+rms_file->NrFreqChan*pulsenr));
 if(extended) {
   newindex_T_rms = rms_file->NrBins*(5+output_nr_pols*(freqnr+rms_file->NrFreqChan*pulsenr));
 }
      }else {
 sindex_I_rms = sindex_I;
 sindex_Q_rms = sindex_Q;
 sindex_U_rms = sindex_U;
 sindex_V_rms = sindex_V;
 newindex_L_rms = newindex_L;
 if(extended) {
   newindex_T_rms = newindex_T;
 }
      }
      if(normalize == 0) {
 ymax = 1;
      }else {
 ymax = datafile->data[sindex_I];
 for(j = 1; j < (datafile->NrBins); j++) {
   if(datafile->data[sindex_I+j] > ymax)
     ymax = datafile->data[sindex_I+j];
 }
      }
      if(ymax == 0)
 ymax = 1;
      for(j = 0; j < (datafile->NrBins); j++) {
 datafile->data[sindex_I + j] /= ymax;
 datafile->data[sindex_Q + j] /= correctQV*ymax;
 datafile->data[sindex_U + j] /= ymax;
 datafile->data[sindex_V + j] /= correctV*correctQV*ymax;
 newdata[j+newindex_L] = sqrt((datafile->data[sindex_Q+j])*(datafile->data[sindex_Q+j])+(datafile->data[sindex_U+j])*(datafile->data[sindex_U+j]));
 if(extended) {
   newdata[j+newindex_T] = sqrt(newdata[j+newindex_L]*newdata[j+newindex_L]+datafile->data[sindex_V+j]*datafile->data[sindex_V+j]);
 }
      }
      if(rms_file_specified) {
 for(j = 0; j < (rms_file->NrBins); j++) {
   rms_file->data[sindex_I_rms + j] /= ymax;
   rms_file->data[sindex_Q_rms + j] /= correctQV*ymax;
   rms_file->data[sindex_U_rms + j] /= ymax;
   rms_file->data[sindex_V_rms + j] /= correctV*correctQV*ymax;
   newdata_rms[j+newindex_L_rms] = sqrt((rms_file->data[sindex_Q_rms+j])*(rms_file->data[sindex_Q_rms+j])+(rms_file->data[sindex_U_rms+j])*(rms_file->data[sindex_U_rms+j]));
   if(extended) {
     newdata_rms[j+newindex_T_rms] = sqrt(newdata[j+newindex_L_rms]*newdata[j+newindex_L_rms]+rms_file->data[sindex_V_rms+j]*rms_file->data[sindex_V_rms+j]);
   }
 }
      }
      datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = 0;
      datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = 0;
      datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = 0;
      datafile->offpulse_rms[3+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = -1;
      datafile->offpulse_rms[4+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = -1;
      if(extended) {
 datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = 0;
 datafile->offpulse_rms[6+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = -1;
 datafile->offpulse_rms[7+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = -1;
      }
      baseline_intensity = 0;
      RMSQ = 0;
      RMSU = 0;
      NrOffpulseBins = 0;
      for(i = 0; i < (rms_file->NrBins); i++) {
 Loffpulse[i] = 0;
 if(checkRegions(i, &onpulse, 0, verbose) == 0) {
   NrOffpulseBins++;
   baseline_intensity += rms_file->data[sindex_I_rms+i];
   RMSQ += (rms_file->data[sindex_Q_rms+i])*(rms_file->data[sindex_Q_rms+i]);
   RMSU += (rms_file->data[sindex_U_rms+i])*(rms_file->data[sindex_U_rms+i]);
   datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] += (rms_file->data[sindex_I_rms+i])*(rms_file->data[sindex_I_rms+i]);
   datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] += (newdata_rms[i+newindex_L_rms])*(newdata_rms[i+newindex_L_rms]);
   datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] += (rms_file->data[sindex_V_rms+i])*(rms_file->data[sindex_V_rms+i]);
   if(extended) {
     datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] += (newdata_rms[i+newindex_L_rms])*(newdata_rms[i+newindex_L_rms]) + (rms_file->data[sindex_V_rms+i])*(rms_file->data[sindex_V_rms+i]);
   }
   Loffpulse[NrOffpulseBins-1] = newdata_rms[i+newindex_L_rms];
   if(extended)
     Poffpulse[NrOffpulseBins-1] = newdata_rms[i+newindex_T_rms];
 }
      }
      baseline_intensity /= (float)NrOffpulseBins;
      RMSQ = sqrt(RMSQ/(float)NrOffpulseBins);
      RMSU = sqrt(RMSU/(float)NrOffpulseBins);
      datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = sqrt(datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]/(float)NrOffpulseBins);
      datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = sqrt(datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]/(float)NrOffpulseBins);
      datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = sqrt(datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]/(float)NrOffpulseBins);
      if(extended) {
 datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = sqrt(datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]/(float)NrOffpulseBins);
      }
      if(rms_file_specified) {
 float scale = 1.0/sqrt(rebin_factor);
 RMSQ *= scale;
 RMSU *= scale;
 datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] *= scale;
 datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] *= scale;
 datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] *= scale;
 if(extended) {
   datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] *= scale;
 }
      }
      if(verbose.verbose) {
 if((freqnr == 0 && pulsenr == 0) || verbose.debug) {
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "  PA conversion output for subint %ld frequency channel %ld:\n", pulsenr, freqnr);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    Average baseline Stokes I: %f\n", baseline_intensity);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS I:                  %f\n", datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS Q:                  %f\n", RMSQ);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS U:                  %f\n", RMSU);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS V:                  %f\n", datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS L (before de-bias): %f\n", datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]);
   if(extended) {
     for(indent = 0; indent < verbose.indent; indent++) printf(" ");
     fprintf(stdout, "    RMS sqrt(Q^2+U^2+V^2):  %f\n", datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]);
   }
 }
      }
      gsl_sort_float (Loffpulse, 1, NrOffpulseBins);
      medianL = gsl_stats_float_median_from_sorted_data(Loffpulse, 1, NrOffpulseBins);
      fflush(stdout);
      if((verbose.verbose && freqnr == 0 && pulsenr == 0) || verbose.debug) {
 for(indent = 0; indent < verbose.indent; indent++) printf(" ");
 fprintf(stdout, "    Median L: %f\n", medianL);
      }
      if(extended) {
 gsl_sort_float (Poffpulse, 1, NrOffpulseBins);
 medianP = gsl_stats_float_median_from_sorted_data(Poffpulse, 1, NrOffpulseBins);
 fflush(stdout);
 if((verbose.verbose && freqnr == 0 && pulsenr == 0) || verbose.debug) {
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    Median sqrt(Q^2+U^2+V^2): %f\n", medianP);
 }
      }
      for(j = 0; j < (datafile->NrBins); j++) {
 if(correctLbias == 1) {
   float junk = (0.5*(RMSQ+RMSU)/newdata[j+newindex_L]);
   if(junk < 1)
     newdata[j+newindex_L] *= sqrt(1.0-junk*junk);
   else
     newdata[j+newindex_L] = 0.0;
 }else if(correctLbias == 0) {
   newdata[j+newindex_L] -= medianL;
 }
 if(extended)
   newdata[j+newindex_T] -= medianP;
      }
      for(i = 0; i < (datafile->NrBins); i++) {
 newdata[i+newindex_Pa] = 90.0*atan2(datafile->data[sindex_U+i],datafile->data[sindex_Q+i])/M_PI;
 if(paoffset) {
   newdata[i+newindex_Pa] += paoffset;
   newdata[i+newindex_Pa] = derotate_180_small_double(newdata[i+newindex_Pa]);
 }
 if(datafile->data[i+sindex_Q] != 0 || datafile->data[i+sindex_U] != 0) {
   newdata[i+newindex_dPa] = sqrt((datafile->data[sindex_Q+i]*RMSU)*(datafile->data[sindex_Q+i]*RMSU) + (datafile->data[sindex_U+i]*RMSQ)*(datafile->data[sindex_U+i]*RMSQ));
   newdata[i+newindex_dPa] /= 2.0*(datafile->data[i+sindex_Q]*datafile->data[i+sindex_Q] + datafile->data[i+sindex_U]*datafile->data[i+sindex_U]);
   newdata[i+newindex_dPa] *= 180.0/M_PI;
 }else {
   newdata[i+newindex_dPa] = 0;
 }
 if(extended) {
   newdata[i+newindex_Ell] = 0;
     newdata[i+newindex_dEll] = 0;
 }
      }
      for(j = 0; j < (datafile->NrBins); j++) {
 newdata[j+newindex_I] = datafile->data[j+sindex_I];
 newdata[j+newindex_V] = datafile->data[j+sindex_V];
      }
    }
  }
  free(datafile->data);
  datafile->data = newdata;
  if(rms_file_specified) {
    free(newdata_rms);
  }
  if(nolongitudes == 0) {
    datafile->tsampMode = TSAMPMODE_LONGITUDELIST;
  }
  datafile->NrPols = output_nr_pols;
  if(extended) {
    datafile->poltype = POLTYPE_ILVPAdPATEldEl;
  }else {
    datafile->poltype = POLTYPE_ILVPAdPA;
  }
  free(Loffpulse);
  free(Poffpulse);
  return 1;
}
int writePPOLHeader(datafile_definition datafile, int argc, char **argv, verbose_definition verbose)
{
  char *txt;
  txt = malloc(10000);
  if(txt == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR writePPOLHeader: Memory allocation error.");
    return 0;
  }
  constructCommandLineString(txt, 10000, argc, argv, verbose);
  fprintf(datafile.fptr_hdr, "#ppol file: %s\n", txt);
  free(txt);
  return 1;
}
int readPPOLHeader(datafile_definition *datafile, int extended, verbose_definition verbose)
{
  float dummy_float;
  int ret, maxlinelength, nrwords;
  char *txt, *ret_ptr, *word_ptr;
  maxlinelength = 2000;
  txt = malloc(maxlinelength);
  if(txt == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: Memory allocation error.");
    return 0;
  }
  datafile->isFolded = 1;
  datafile->foldMode = FOLDMODE_FIXEDPERIOD;
  datafile->fixedPeriod = 0;
  datafile->tsampMode = TSAMPMODE_LONGITUDELIST;
  datafile->fixedtsamp = 0;
  datafile->tsubMode = TSUBMODE_FIXEDTSUB;
  if(datafile->tsub_list != NULL)
    free(datafile->tsub_list);
  datafile->tsub_list = (double *)malloc(sizeof(double));
  if(datafile->tsub_list == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: Memory allocation error");
    return 0;
  }
  datafile->tsub_list[0] = 0;
  datafile->NrSubints = 1;
  datafile->NrFreqChan = 1;
  datafile->datastart = 0;
  rewind(datafile->fptr);
  ret = fread(txt, 1, 3, datafile->fptr);
  txt[3] = 0;
  if(ret != 3) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: cannot read from file.");
    free(txt);
    return 0;
  }
  if(strcmp(txt, "#pp") != 0
) {
    fflush(stdout);
    printwarning(verbose.debug, "WARNING readPPOLHeader: File does not appear to be in PPOL or PPOLSHORT format. I will try to load file, but this will probably fail. Did you run ppol first?");
  }
  skipallhashedlines(datafile);
  datafile->NrBins = 0;
  dummy_float = 0;
  do {
    ret_ptr = fgets(txt, maxlinelength, datafile->fptr);
    if(ret_ptr != NULL) {
      if(txt[0] != '#') {
 if(extended) {
   word_ptr = pickWordFromString(txt, 2, &nrwords, 1, ' ', verbose);
   if(nrwords != 10 && nrwords != 14) {
     fflush(stdout);
     printerror(verbose.debug, "ERROR readPPOLHeader: Line should have 10 or 14 words, got %d", nrwords);
     if(nrwords == 3)
       printerror(verbose.debug, "                             Maybe file is in format %s?", returnFileFormat_str(PPOL_SHORT_format));
     printerror(verbose.debug, "                             Line: '%s'.", txt);
     free(txt);
     return 0;
   }
   if(nrwords == 10) {
     datafile->poltype = POLTYPE_ILVPAdPA;
     datafile->NrPols = 5;
   }else {
     datafile->poltype = POLTYPE_ILVPAdPATEldEl;
     datafile->NrPols = 8;
   }
 }else {
   word_ptr = pickWordFromString(txt, 1, &nrwords, 1, ' ', verbose);
   if(nrwords != 3) {
     fflush(stdout);
     printerror(verbose.debug, "ERROR readPPOLHeader: Line should have 3 words, got %d", nrwords);
     if(nrwords == 10)
       printerror(verbose.debug, "                             Maybe file is in format %s?", returnFileFormat_str(PPOL_format));
     printerror(verbose.debug, "                             Line: '%s'.", txt);
     free(txt);
     return 0;
   }
 }
 ret = sscanf(word_ptr, "%f", &dummy_float);
 if(ret != 1) {
   fflush(stdout);
   printerror(verbose.debug, "ERROR readPPOLHeader: Cannot interpret as a float: '%s'.", txt);
   free(txt);
   return 0;
 }
 if(dummy_float >= 360) {
   fflush(stdout);
   printwarning(verbose.debug, "WARNING: IGNORING POINTS AT PULSE LONGITUDES > 360 deg.");
 }else {
   (datafile->NrBins)++;
 }
      }
    }
  }while(ret_ptr != NULL && dummy_float < 360);
  if(extended == 0) {
    datafile->poltype = POLTYPE_PAdPA;
    datafile->NrPols = 2;
  }
  fflush(stdout);
  if(verbose.verbose) fprintf(stdout, "Going to load %ld points from %s\n", datafile->NrBins, datafile->filename);
  if(datafile->NrBins == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: No data in %s", datafile->filename);
    free(txt);
    return 0;
  }
  fseek(datafile->fptr, datafile->datastart, SEEK_SET);
  free(txt);
  if(datafile->offpulse_rms != NULL) {
    free(datafile->offpulse_rms);
    datafile->offpulse_rms = NULL;
  }
  if(extended) {
    datafile->offpulse_rms = (float *)malloc(datafile->NrSubints*datafile->NrFreqChan*datafile->NrPols*sizeof(float));
    if(datafile->offpulse_rms == NULL) {
      fflush(stdout);
      printerror(verbose.debug, "ERROR readPPOLHeader: Memory allocation error");
      return 0;
    }
  }
  datafile->tsamp_list = (double *)malloc(datafile->NrBins*sizeof(double));
  if(datafile->tsamp_list == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: Memory allocation error");
    return 0;
  }
  return 1;
}
int readPPOLfile(datafile_definition *datafile, float *data, int extended, float add_longitude_shift, verbose_definition verbose)
{
  int maxlinelength;
  long i, k, dummy_long;
  char *txt, *ret_ptr;
  if(datafile->NrBins == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLfile: No data in %s", datafile->filename);
    return 0;
  }
  maxlinelength = 2000;
  txt = malloc(maxlinelength);
  if(txt == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLfile: Memory allocation error.");
    return 0;
  }
  fseek(datafile->fptr, datafile->datastart, SEEK_SET);
  k = 0;
  if(extended) {
    datafile->offpulse_rms[3] = -1;
    datafile->offpulse_rms[4] = -1;
    if(datafile->NrPols == 8) {
      datafile->offpulse_rms[6] = -1;
      datafile->offpulse_rms[7] = -1;
    }
  }
  for(i = 0; i < datafile->NrBins; i++) {
    ret_ptr = fgets(txt, maxlinelength, datafile->fptr);
    if(ret_ptr == NULL) {
      fflush(stdout);
      printerror(verbose.debug, "ERROR readPPOLfile: Cannot read next line, should not happen after successfully reading in header");
      free(txt);
      return 0;
    }
    if(txt[0] != '#') {
      if(extended == 0) {
 sscanf(txt, "%lf %f %f", &(datafile->tsamp_list[k]), &(data[k]), &(data[k+datafile->NrBins]));
      }else {
 if(datafile->NrPols == 8) {
   sscanf(txt, "%ld %lf %f %f %f %f %f %f %f %f %f %f %f %f", &dummy_long, &(datafile->tsamp_list[k]), &(data[k]), &(datafile->offpulse_rms[0]), &(data[k+datafile->NrBins]), &(datafile->offpulse_rms[1]), &(data[k+2*datafile->NrBins]), &(datafile->offpulse_rms[2]), &(data[k+3*datafile->NrBins]), &(data[k+4*datafile->NrBins]), &(data[k+5*datafile->NrBins]), &(datafile->offpulse_rms[5]), &(data[k+6*datafile->NrBins]), &(data[k+7*datafile->NrBins]));
 }else {
   sscanf(txt, "%ld %lf %f %f %f %f %f %f %f %f", &dummy_long, &(datafile->tsamp_list[k]), &(data[k]), &(datafile->offpulse_rms[0]), &(data[k+datafile->NrBins]), &(datafile->offpulse_rms[1]), &(data[k+2*datafile->NrBins]), &(datafile->offpulse_rms[2]), &(data[k+3*datafile->NrBins]), &(data[k+4*datafile->NrBins]));
 }
      }
      datafile->tsamp_list[k] += add_longitude_shift;
      if(datafile->tsamp_list[k] >= 0 && datafile->tsamp_list[k] < 360) {
   k++;
      }else {
 fflush(stdout);
 printwarning(verbose.debug, "WARNING readPPOLfile: IGNORING POINTS AT PULSE LONGITUDES > 360 deg.");
      }
    }
  }
  if(k != datafile->NrBins) {
    fflush(stdout);
    printerror(verbose.debug, "WARNING readPPOLfile: The nr of bins read in is different as determined from header. Something is wrong.");
    return 0;
  }
  fflush(stdout);
  if(verbose.verbose) fprintf(stdout, "readPPOLfile: Accepted %ld points\n", datafile->NrBins);
     free(txt);
  return 1;
}
int writePPOLfile(datafile_definition datafile, float *data, int extended, int onlysignificantPA, int twoprofiles, float PAoffset, verbose_definition verbose)
{
  long j;
  if(datafile.poltype != POLTYPE_ILVPAdPA && datafile.poltype != POLTYPE_PAdPA && datafile.poltype != POLTYPE_ILVPAdPATEldEl) {
    printerror(verbose.debug, "ERROR writePPOLfile: Data doesn't appear to have poltype ILVPAdPA, PAdPA or ILVPAdPATEldEl (it is %d).", datafile.poltype);
    return 0;
  }
  if(datafile.poltype == POLTYPE_ILVPAdPA && datafile.NrPols != 5) {
    printerror(verbose.debug, "ERROR writePPOLfile: 5 polarization channels were expected, but there are %ld.", datafile.NrPols);
    return 0;
  }else if(datafile.poltype == POLTYPE_PAdPA && datafile.NrPols != 2) {
    printerror(verbose.debug, "ERROR writePPOLfile: 2 polarization channels were expected, but there are %ld.", datafile.NrPols);
    return 0;
  }else if(datafile.poltype == POLTYPE_ILVPAdPATEldEl && datafile.NrPols != 8) {
    printerror(verbose.debug, "ERROR writePPOLfile: 8 polarization channels were expected, but there are %ld.", datafile.NrPols);
    return 0;
  }
  if(datafile.NrSubints > 1 || datafile.NrFreqChan > 1) {
    printerror(verbose.debug, "ERROR writePPOLfile: Can only do this opperation if there is one subint and one frequency channel.");
    return 0;
  }
  if(datafile.tsampMode != TSAMPMODE_LONGITUDELIST) {
    printerror(verbose.debug, "ERROR writePPOLfile: Expected pulse longitudes to be defined.");
    return 0;
  }
  int pa_offset, dpa_offset;
  if(datafile.poltype == POLTYPE_ILVPAdPA || datafile.poltype == POLTYPE_ILVPAdPATEldEl) {
    pa_offset = 3;
    dpa_offset = 4;
  }else if(datafile.poltype == POLTYPE_PAdPA) {
    pa_offset = 0;
    dpa_offset = 1;
  }
  for(j = 0; j < datafile.NrBins; j++) {
    if(data[j+dpa_offset*datafile.NrBins] > 0 || onlysignificantPA == 0) {
      if(extended) {
 fprintf(datafile.fptr, "%ld %e %e %e %e %e %e %e %e %e", j, datafile.tsamp_list[j], data[j], datafile.offpulse_rms[0], data[j+datafile.NrBins], datafile.offpulse_rms[1], data[j+2*datafile.NrBins], datafile.offpulse_rms[2], data[j+pa_offset*datafile.NrBins]+PAoffset, data[j+dpa_offset*datafile.NrBins]);
 if(datafile.poltype == POLTYPE_ILVPAdPATEldEl)
   fprintf(datafile.fptr, " %e %e %e %e", data[j+5*datafile.NrBins], datafile.offpulse_rms[5], data[j+6*datafile.NrBins], data[j+7*datafile.NrBins]);
 fprintf(datafile.fptr, "\n");
      }else {
 fprintf(datafile.fptr, "%e %e %e\n", datafile.tsamp_list[j], data[j+pa_offset*datafile.NrBins]+PAoffset, data[j+dpa_offset*datafile.NrBins]);
      }
    }
  }
  if(twoprofiles) {
    for(j = 0; j < datafile.NrBins; j++) {
      if(data[j+dpa_offset*datafile.NrBins] > 0 || onlysignificantPA == 0) {
 if(extended) {
   fprintf(datafile.fptr, "%ld %e %e %e %e %e %e %e %e %e", j, datafile.tsamp_list[j]+360, data[j], datafile.offpulse_rms[0], data[j+datafile.NrBins], datafile.offpulse_rms[1], data[j+2*datafile.NrBins], datafile.offpulse_rms[2], data[j+pa_offset*datafile.NrBins]+PAoffset, data[j+dpa_offset*datafile.NrBins]);
   if(datafile.poltype == POLTYPE_ILVPAdPATEldEl)
     fprintf(datafile.fptr, " %e %e %e %e", data[j+5*datafile.NrBins], datafile.offpulse_rms[5], data[j+6*datafile.NrBins], data[j+7*datafile.NrBins]);
   fprintf(datafile.fptr, "\n");
 }else {
   fprintf(datafile.fptr, "%e %e %e\n", datafile.tsamp_list[j]+360, data[j]+PAoffset, data[j+dpa_offset*datafile.NrBins]);
 }
      }
    }
  }
  return 1;
}