/*
    Theseus - maximum likelihood superpositioning of macromolecular structures

    Copyright (C) 2004-2010 Douglas L. Theobald

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the:

    Free Software Foundation, Inc.,
    59 Temple Place, Suite 330,
    Boston, MA  02111-1307  USA

    -/_|:|_|_\-
*/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <float.h>
#include <ctype.h>
#include <gsl/gsl_sf_gamma.h>
#include "CovMat.h"
#include "DLTutils.h"
#include "Error.h"
#include "pdbIO.h"
#include "pdbMalloc.h"
#include "Cds.h"
#include "PDBCds.h"
#include "pdbStats.h"
#include "pdbUtils.h"
#include "Embed.h"
#include "DLTmath.h"
#include "distfit.h"
#include "HierarchVars.h"


static int
findsmallest(double *vec, const int len)
{
    int         i, smi = 0;
    double      sm;

    sm = DBL_MAX;
    for (i = 0; i < len; ++i)
    {
        if (vec[i] < sm)
        {
            sm = vec[i];
            smi = i;
        }
    }

    return(smi);
}


static int
findsmallest_gt(double *vec, const int len, const double gt)
{
    int         i, smi = 0;
    double      sm;

    sm = DBL_MAX;
    for (i = 0; i < len; ++i)
    {
        if (vec[i] < sm && vec[i] > gt)
        {
            sm = vec[i];
            smi = i;
        }
    }

    return(smi);
}

static double
HarmonicAve(const double *data, const int len)
{
    int             i;
    double          invdata;

    invdata = 0.0;
    for (i = 0; i < len; ++i)
        invdata += 1.0 / data[i];

    return(len / invdata);
}


double
HarmonicAveBayes(const double *data, const int len, const double pr)
{
    int             i;
    double          invdata;

    invdata = 0.0;
    for (i = 0; i < len; ++i)
        invdata += 1.0 / data[i];

    invdata += 1.0 / pr;

    return((len +2.0 ) / invdata);
}


double
LogarithmicAve(const double *data, const int len)
{
    int             i;
    double          logdata;

    logdata = 0.0;
    for (i = 0; i < len; ++i)
        logdata += log(data[i]);

    return(logdata / len);
}


void
InvgaussFitVars(CdsArray *cdsA, double *mean, double *lambda)
{
    double          sum_mean, sum_lambda, invmean, var;
    const double   *data = (const double *) cdsA->var;
    const int       num = cdsA->vlen;
    const double    numd = (double) num;
    int             i;

    sum_mean = 0.0;
    for (i = 0; i < num; ++i)
        sum_mean += data[i];

    *mean = sum_mean / numd; /* MLE and MME */
    /* invmean = 1.0 / *mean; */
    invmean = numd / *mean;

    sum_lambda = var = 0.0;
    for (i = 0; i < num; ++i)
    {
        sum_lambda += (1.0 / data[i] - invmean);
        var += mysquare(data[i] - *mean);
    }
    var /= numd;
    *lambda = numd / sum_lambda; /* MLE */

/*     sum_lambda = var = 0.0; */
/*     for (i = 0; i < num; ++i) */
/*     { */
/*         sum_lambda += (mysquare(data[i] - *mean) / data[i]); */
/*         var += mysquare(data[i] - *mean); */
/*     } */
/*     var /= numd; */
/*     *lambda = numd * *mean * *mean / sum_lambda; */

    printf("MLE: %10.5f\n", *lambda);
    *lambda = *mean * *mean * *mean / var; /* MME */
    printf(" MME: %10.5f", *lambda);

    cdsA->stats->hierarch_p1 = *mean;
    cdsA->stats->hierarch_p2 = *lambda;
}


void
InvgaussAdjustVars(CdsArray *cdsA,
                   const double mean, const double lambda)
{
    int             i;
    double         *variance = cdsA->var;
    const int       vlen = cdsA->vlen;

    for (i = 0; i < vlen; ++i)
    {
        /* printf("\n %10.5f", variance[i]); */
        variance[i] =
        (0.5 * mean / lambda) *
        (
            sqrt(
                  ((9.0 * mean * mean) * mysquare(vlen + 1) +
                   (4.0 * lambda * lambda)) +
                  (12.0 * vlen * lambda) * variance[i]
            )
            - 3.0 * mean * (1 + vlen)
        );
       /*  printf(" %10.5f", variance[i]); */
    }
}


/* fit a lognormal distribution by maximum likelihood */
void
LognormalFitVars(CdsArray *cdsA, double *zeta, double *sigma)
{
    const double   *data = (const double *) cdsA->var;
    const int       num = cdsA->vlen;
    double          ave, var;
    int             i;

    ave = 0.0;
    for (i = 0; i < num; ++i)
    {
        if (data[i] < DBL_EPSILON)
            continue;
        ave += log(data[i]);
    }
    ave /= (double) num;

    var = 0.0;
    for (i = 0; i < num; ++i)
    {
        if (data[i] < DBL_EPSILON)
            var += mysquare(ave);
        else
            var += mysquare(log(data[i]) - ave);
    }
    var /= (double) num - 1;

    *zeta = ave;
    *sigma = sqrt(var);

    cdsA->stats->hierarch_p1 = *zeta;
    cdsA->stats->hierarch_p2 = *sigma;
}


static void
evallognormal(double var, const double zeta, const double sigma,
              const double varML, const int num, double *fx, double *dfx)
{
    *fx = (1.0 + zeta/sigma + 1.5*num + log(var)/sigma) * var - (1.5 * num * varML);
    *dfx = 1.0 + (zeta + 1.0)/sigma + 1.5 * num + log(var)/sigma;
}


void
LognormalAdjustVars(CdsArray *cdsA, double zeta, double sigma)
{
    double          var, varML, fx, dfx;
    int             i, j;
    double          tol = cdsA->algo->precision;
    double         *variance = cdsA->var;
    const int       num = cdsA->vlen;

    for (i = 0; i < num; ++i)
    {
        /* Use Newton-Raphson to find ML estimate of lognormally distributed
           variance.

           must find root of:

              F1 = (1 + zeta/sigma + 1.5 num + ln(x)/sigma)x -1.5 num variance_ML = 0

           where the first derivative with repect to the lognormal variance
           estimate x (dF1/dx) is:

              F1' = 1 + (zeta + 1)/sigma + 1.5 num + ln(x)/sigma = 0
        */
        var = varML = variance[i]; /* initial guess */
        printf(" %10.5f\n", variance[i]);

        for (j = 0; j < 200; ++j)
        {
            evallognormal(var, zeta, sigma, varML, num, &fx, &dfx);

            if (fabs(fx) < tol)
                break; /* success */

            var -= (fx / dfx); /* Newton-Raphson correction */
        }

        if (j != 200)
            variance[i] = var;

        printf(" %10.5f", variance[i]);
    }
}


void
InvGammaFitVars(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2, logL;
    int             i, j, count, newlen, df;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->covweight != 0)
    {
        evecs = cdsA->tmpmatKK2;

        if (vlen - 1 < nd - 3)
            newlen = vlen - 1;
        else
            newlen = nd - 3;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);

        RevVecIp(variance, vlen);

        for (i = newlen; i < vlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;

        for (i = 0; i < vlen; ++i)
            newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        count = 0;
        do
        {
            ++count;
            oldb = b;
            oldc = c;

            chi2 = invgamma_fit(newvar, newlen, &b, &c, &logL);

            for (i = 0; i < newlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        InvGammaAdjustCov(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];
    }
    else
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;

        for (i = 0; i < vlen; ++i)
            newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            chi2 = invgamma_fit(newvar, vlen, &b, &c, &logL);

            if (cdsA->algo->alignment == 1)
            {
                for (i = 0; i < vlen; ++i)
                {
                    df = 0;
                    for (j = 0; j < cnum; ++j)
                        df += cdsA->cds[j]->o[i];

                    df *= 3;

                    newvar[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
                }
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                {
                    newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
                }

            }

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count++, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                   count++, b, c, b / (c+1.0), logL, chi2);
            fflush(NULL);
        }

        memcpy(variance, newvar, vlen * sizeof(double));
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


static void
InvGammaAdjustVar(double *newvar, const int vlen, const int cnum,
                  double *var, const double b, const double c)
{
    int         i;

    for (i = 0; i < vlen; ++i)
        //newvar[i] = (3.0*cnum*var[i] + 2.0*b) / (3.0*cnum + 2.0 + 2.0*c); // this is the conditional maximization algorithm
        newvar[i] = (3.0*cnum*var[i] + 2.0*b) / (3.0*cnum + 2.0*c); // this is required for an EM algorithm of the variances
}


static void
InvGammaAdjustVarNoN(double *newvar, const int vlen, const int cnum,
                     double *var, const double b, const double c)
{
    int         i;

    for (i = 0; i < vlen; ++i)
        newvar[i] = (3.0*var[i] + 2.0*b) / (3.0 + 2.0*(1.0 + c));
}


static void
InvGammaAdjustVarOcc(Cds **cds, double *newvar, const int vlen, const int cnum,
                     double *var, const double b, const double c)
{
    int         i, j;
    double      df;

    for (i = 0; i < vlen; ++i)
    {
        df = 0;
        for (j = 0; j < cnum; ++j)
            df += cds[j]->o[i];

        df *= 3;

        newvar[i] = (df*var[i] + 2.0*b) / (df + 2.0*(1.0 + c));
    }
}


void
ConjBayesAdjustVar(double *newvar, const double *var, const int vlen, const int cnum, const double phi)
{
    int         i;

    for (i = 0; i < vlen; ++i)
    {
        printf("\n%d -- bvar:%f  ", i, var[i]);
        newvar[i] = (cnum + 1.0) / (1.0 / phi + cnum / var[i]);
        //newvar[i] = 2.0 / (1.0 / phi + 1.0 / var[i]);
        printf("avar:%f", newvar[i]);
        printf("\nphi:%f\n", phi);
    }
    fflush(NULL);
}


/* Adjustment assuming an inverse wishart prior, empirical bayes for the variance
   phi * I, a diagonal homoskedastic covariance matrix.  Three degrees of freedom,
   one for each dimension.
*/
void
WishartAdjustCov(double *newvar, const double *var, const int vlen, const int cnum, const double phi)
{
    int         i;
    double      nd = 3.0 * cnum;
    double      df = 3.0;

    for (i = 0; i < vlen; ++i)
    {
        printf("\n%d -- bvar:%f  ", i, var[i]);
        newvar[i] = (df * phi + nd * var[i]) / (df + nd + vlen + 1);
        printf("avar:%f", newvar[i]);
        printf("\nphi:%f\n", phi);
    }
    fflush(NULL);
}


/* When assuming a diagonal inverse wishart matrix, the pdf reduces to the 
   product of scale inverse chi-squares (no need for vlen in denom). */
void
WishartAdjustVar(double *newvar, const double *var, const int vlen, const int cnum, const double phi)
{
    int         i;
    double      nd = 3.0 * cnum;
//    double      df = 3.0;

    for (i = 0; i < vlen; ++i)
    {
        printf("\n%d -- bvar:%f  ", i, var[i]);
        //newvar[i] = (df * phi + nd * var[i]) / (df + nd + 1);
        newvar[i] = (phi + nd * var[i]) / (nd - 1.0);
        //newvar[i] = (3.0*phi + nd*var[i]) / (nd - 3.0);
        printf("avar:%f", newvar[i]);
        printf("\nphi:%f\n", phi);
    }
    fflush(NULL);
}


/* Assumes that each structure has its own Wishart prior, proportional to the identity,
   and that all the priors are equal */
void
WishartAdjustVar2(double *newvar, const double *var, const int vlen, const int cnum, const double phi)
{
    int         i;
//    double      nd = 3.0 * cnum;
//    double      df = 3.0;

    for (i = 0; i < vlen; ++i)
    {
        printf("\n%d -- bvar:%f  ", i, var[i]);
        //newvar[i] = (df * phi + nd * var[i]) / (df + nd + 1);
        newvar[i] = (3.0*phi + var[i]) / 2.0;
        printf("avar:%f", newvar[i]);
        printf("\nphi:%f\n", phi);
    }
    fflush(NULL);
}


void
WishartFitVar(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *var = cdsA->var;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          phi = 0.0, oldphi;
    int             i, count;

    newvar = malloc(vlen * sizeof(double));

    memcpy(newvar, var, vlen * sizeof(double));

    count = 0;
    do
    {
        oldphi = phi;

        phi = 0.0;
        for (i = 0; i < vlen; ++i)
            phi += 1.0 / newvar[i];

        //phi = vlen / phi;
        phi = (vlen - 2.0) / phi;
        //phi = 1.0;

        WishartAdjustVar(newvar, var, vlen, cnum, phi);
        printf("\n count:%d  oldphi:%f  phi:%f", count, oldphi, phi);

        count++;

        if (iterate == 0 || cdsA->algo->abort == 1)
            break;
    }
    while(fabs(oldphi - phi) > fabs(phi*precision));

    memcpy(var, newvar, vlen * sizeof(double));

    free(newvar);
}


void
WishartFitVar2(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *var = cdsA->var;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          phi = 0.0, oldphi;
    int             i, count;

    newvar = malloc(vlen * sizeof(double));

    memcpy(newvar, var, vlen * sizeof(double));

    count = 0;
    do
    {
        oldphi = phi;

        phi = 0.0;
        for (i = 0; i < vlen; ++i)
            phi += 1.0 / newvar[i];

        phi = (3.0*vlen +6.0*vlen*cnum)/ phi;
        //phi = 1.0;

        WishartAdjustVar2(newvar, var, vlen, cnum, phi);
        printf("\n count:%d  oldphi:%f  phi:%f", count, oldphi, phi);

        count++;

        if (iterate == 0 || cdsA->algo->abort == 1)
            break;
    }
    while(fabs(oldphi - phi) > fabs(phi*precision));

    memcpy(var, newvar, vlen * sizeof(double));

    free(newvar);
}


void
InvGammaFitEvals(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL, harmave, mode;
    int             i, count, newlen;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        newlen = vlen - 3;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            qsort(newvar, vlen, sizeof(double), dblcmp_rev);
            chi2 = invgamma_fit(newvar, newlen, &b, &c, &logL);

            if (2*c + 2 > 1000 * nd)
            {
                harmave = HarmonicAve(newvar + vlen - newlen, newlen);

                for (i = 0; i < vlen; ++i)
                    variance[i] = harmave;

                return;
            }

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVar(newvar, vlen, cnum, variance, b, c);

            /* newvar[findmin(variance, vlen)] = 2.0*b / (nd + 2.0*(1.0 + c)); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        if (cdsA->algo->alignment == 1)
            InvGammaAdjustVarOcc(cdsA->cds, variance, vlen, cnum, variance, b, c);
        else
            InvGammaAdjustVar(variance, vlen, cnum, variance, b, c);
    }
    else if (cdsA->algo->covweight != 0)
    {
        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        evecs = cdsA->tmpmatKK2;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);
        /* eigensym((const double **) cdsA->CovMat, variance, evecs, vlen); */

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
/*         b = c = 0.0; */

        for (i = 0; i < vlen; ++i)
            newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            chi2 = invgamma_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            /* the mode of an inv gamma dist */
            mode = b / (c+1.0);

/*          for (i = 0; i < vlen - newlen; ++i) */
/*              newvar[i] = mode; */
/*             for (i = 0; i < newlen; ++i) */
/*                 printf("\n%3d %e", i, newvar[i]); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        InvGammaAdjustCov(cdsA, b, c);
/*      printf("\n\n count: %d", count); */
/*      for (i = 0; i < vlen; ++i) */
/*          printf("\n%3d %8.3e %8.3e", i, newvar[i], mode); */
/*         EigenReconSym(cdsA->CovMat, (const double **) evecs, (const double *) newvar, vlen); */

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];

/*         printf("\n\n count: %d  harm ave: %8.3e log ave: %8.3e", */
/*                count, HarmonicAve(variance, vlen), exp(LogarithmicAve(variance, vlen))); */
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaFitEvalsNoN(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL, harmave, mode;
    int             i, count, newlen;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        newlen = vlen - 3;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            qsort(newvar, vlen, sizeof(double), dblcmp_rev);
            chi2 = invgamma_fit(newvar, newlen, &b, &c, &logL);

            if (2*c + 2 > 1000 * nd)
            {
                harmave = HarmonicAve(newvar + vlen - newlen, newlen);

                for (i = 0; i < vlen; ++i)
                    variance[i] = harmave;

                return;
            }

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVarNoN(newvar, vlen, cnum, variance, b, c);

            /* newvar[findmin(variance, vlen)] = 2.0*b / (nd + 2.0*(1.0 + c)); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        if (cdsA->algo->alignment == 1)
            InvGammaAdjustVarOcc(cdsA->cds, variance, vlen, cnum, variance, b, c);
        else
            InvGammaAdjustVar(variance, vlen, cnum, variance, b, c);
    }
    else if (cdsA->algo->covweight != 0)
    {
        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        evecs = cdsA->tmpmatKK2;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);
        /* eigensym((const double **) cdsA->CovMat, variance, evecs, vlen); */

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
/*         b = c = 0.0; */

        for (i = 0; i < vlen; ++i)
            newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            chi2 = invgamma_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            /* the mode of an inv gamma dist */
            mode = b / (c+1.0);

/*          for (i = 0; i < vlen - newlen; ++i) */
/*              newvar[i] = mode; */
/*             for (i = 0; i < newlen; ++i) */
/*                 printf("\n%3d %e", i, newvar[i]); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        InvGammaAdjustCov(cdsA, b, c);
/*      printf("\n\n count: %d", count); */
/*      for (i = 0; i < vlen; ++i) */
/*          printf("\n%3d %8.3e %8.3e", i, newvar[i], mode); */
/*         EigenReconSym(cdsA->CovMat, (const double **) evecs, (const double *) newvar, vlen); */

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];

/*         printf("\n\n count: %d  harm ave: %8.3e log ave: %8.3e", */
/*                count, HarmonicAve(variance, vlen), exp(LogarithmicAve(variance, vlen))); */
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


/* set missing evals to the mode of the inv gamma */
void
InvGammaFitModeEvals(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL, harmave, mode;
    int             i, count, newlen;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        newlen = vlen - 3;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            qsort(newvar, vlen, sizeof(double), dblcmp_rev);
            chi2 = invgamma_fit(newvar, newlen, &b, &c, &logL);

            if (2*c + 2 > 100 * nd)
            {
                harmave = HarmonicAve(newvar + vlen - newlen, newlen);

                for (i = 0; i < vlen; ++i)
                    variance[i] = harmave;

                return;
            }

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVar(newvar, vlen, cnum, variance, b, c);

            /* newvar[findmin(variance, vlen)] = 2.0*b / (nd + 2.0*(1.0 + c)); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        if (cdsA->algo->alignment == 1)
            InvGammaAdjustVarOcc(cdsA->cds, variance, vlen, cnum, variance, b, c);
        else
            InvGammaAdjustVar(variance, vlen, cnum, variance, b, c);

        variance[findmin(variance, vlen)] = b/(1.0 + c);
    }
    else if (cdsA->algo->covweight != 0)
    {
        int negevals = 3;
        if (vlen - negevals < nd - 3 - negevals)
            newlen = vlen - negevals;
        else
            newlen = nd - 3 - negevals;

        evecs = cdsA->tmpmatKK2;
        printf("\n\n count:");
        /* eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen); */

/*         MatPrint(cdsA->CovMat, cdsA->vlen); */
/*         eigensym((const double **) cdsA->CovMat, variance, evecs, vlen); */
/*         EigenReconSym(cdsA->CovMat, (const double **) evecs, (const double *) variance, vlen); */
/*         MatPrint(cdsA->CovMat, cdsA->vlen); */

        eigensym((const double **) cdsA->CovMat, variance, evecs, vlen);

        for (i = 0; i < vlen; ++i)
            printf("\nEVALS %3d %e", i, variance[i]);

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
/*         b = c = 0.0; */

        for (i = 0; i < vlen; ++i)
            newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            /* chi2 = invgamma_bayes_fit(newvar + vlen - newlen, newlen, &b, &c, &logL); */
            chi2 = invgamma_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            /* the mode of an inv gamma dist */
            mode = b / (c+1.0);
            //mode = b/(c-1.0); /* mean */

            for (i = 0; i < vlen - newlen; ++i)
                newvar[i] = mode;

            for (i = 0; i < vlen; ++i)
                printf("\n%3d %e", i, newvar[i]);

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

/*         InvGammaAdjustCovMode(cdsA, b, c); */
/*      printf("\n\n count: %d", count); */
        for (i = 0; i < vlen; ++i)
            printf("\n%3d %8.3e %8.3e", i, newvar[i], mode);
        /* RevVecIp(variance, vlen); */
        EigenReconSym(cdsA->CovMat, (const double **) evecs, (const double *) newvar, vlen);
        /* MatPrint(cdsA->CovMat, cdsA->vlen); */

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];

/*         printf("\n\n count: %d  harm ave: %8.3e log ave: %8.3e", */
/*                count, HarmonicAve(variance, vlen), exp(LogarithmicAve(variance, vlen))); */
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaBayesFitEvals(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL, harmave, mode;
    int             i, count, newlen;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        newlen = vlen - 3;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            qsort(newvar, vlen, sizeof(double), dblcmp_rev);
            chi2 = invgamma_bayes_fit(newvar, newlen, &b, &c, &logL);

            if (2*c + 2 > 100 * nd)
            {
                harmave = HarmonicAve(newvar + vlen - newlen, newlen);

                for (i = 0; i < vlen; ++i)
                    variance[i] = harmave;

                return;
            }

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVar(newvar, vlen, cnum, variance, b, c);

            /* newvar[findmin(variance, vlen)] = 2.0*b / (nd + 2.0*(1.0 + c)); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        if (cdsA->algo->alignment == 1)
            InvGammaAdjustVarOcc(cdsA->cds, variance, vlen, cnum, variance, b, c);
        else
            InvGammaAdjustVar(variance, vlen, cnum, variance, b, c);
    }
    else if (cdsA->algo->covweight != 0)
    {
        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        evecs = cdsA->tmpmatKK2;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);
        /* eigensym((const double **) cdsA->CovMat, variance, evecs, vlen); */

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
/*         b = c = 0.0; */

        for (i = 0; i < vlen; ++i)
            newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            chi2 = invgamma_bayes_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            /* the mode of an inv gamma dist */
            mode = b / (c+1.0);

/*          for (i = 0; i < vlen - newlen; ++i) */
/*              newvar[i] = mode; */
/*             for (i = 0; i < newlen; ++i) */
/*                 printf("\n%3d %e", i, newvar[i]); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        InvGammaAdjustCov(cdsA, b, c);
/*      printf("\n\n count: %d", count); */
/*      for (i = 0; i < vlen; ++i) */
/*          printf("\n%3d %8.3e %8.3e", i, newvar[i], mode); */
/*         EigenReconSym(cdsA->CovMat, (const double **) evecs, (const double *) newvar, vlen); */

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];

/*         printf("\n\n count: %d  harm ave: %8.3e log ave: %8.3e", */
/*                count, HarmonicAve(variance, vlen), exp(LogarithmicAve(variance, vlen))); */
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaAdjustVarBfact_old(Cds **cds, double *newvar, const int vlen, const int cnum,
                       const double *var, const double b, const double c)
{
    int             i, j;
    const double    nd = 3.0 * cnum;
    const double    fact = 1.0 / (8.0 * cnum + 2.0 + 2.0 * c);

    for (i = 0; i < vlen; ++i)
        newvar[i] = nd * var[i] + 2.0 * b;

    for (j = 0; j < cnum; ++j)
        for (i = 0; i < vlen; ++i)
            newvar[i] += cds[j]->bfact_c * 3.0 * cds[j]->prvar[i];

    for (i = 0; i < vlen; ++i)
        newvar[i] *= fact;
}


void
InvGammaAdjustVarBfactOcc_old(CdsArray *cdsA, double *newvar, const int vlen, const int cnum,
                          const double *var, const double b, const double c)
{
    int             i, j;
    Cds **cds = cdsA->cds;

    for (i = 0; i < vlen; ++i)
        newvar[i] = 3.0 * cdsA->df[i] * var[i] + 2.0 * b;

    for (j = 0; j < cnum; ++j)
        for (i = 0; i < vlen; ++i)
            newvar[i] += cds[j]->bfact_c * 3.0 * cds[j]->prvar[i];

    for (i = 0; i < vlen; ++i)
        newvar[i] /= (8.0 * cdsA->df[i] + 2.0 + 2.0 * c);
}


static void
InvGammaAdjustVarBfact(Cds **cds, double *newvar, const int vlen, const int cnum,
                       const double *var, const double b, const double c)
{
    int             i, j;
    const double    nd = 3.0 * cnum;
    //const double    fact = 1.0 / (8.0 * cnum + 2.0 * c + 2.0);
    const double    fact = 1.0 / (nd + cnum + 2.0 * (cnum + 1.0 + c));

    for (i = 0; i < vlen; ++i)
        newvar[i] = nd * var[i] + 2.0 * b;

    for (j = 0; j < cnum; ++j)
        for (i = 0; i < vlen; ++i)
            newvar[i] += 3.0 * cds[j]->bfact_c * cds[j]->prvar[i];

    for (i = 0; i < vlen; ++i)
        newvar[i] *= fact;
}


static void
InvGammaAdjustVarBfactOcc(CdsArray *cdsA, double *newvar, const int vlen, const int cnum,
                          const double *var, const double b, const double c)
{
    int             i, j;
    Cds **cds = cdsA->cds;
    const double    nd = 3.0 * cnum;
    //const double    fact = 1.0 / (8.0 * cnum + 2.0 * c + 2.0);
    const double    fact = 1.0 / (nd + cnum + 2.0 * (cnum + 1.0 + c));

    for (i = 0; i < vlen; ++i)
        newvar[i] = nd * var[i] + 2.0 * b;

    for (j = 0; j < cnum; ++j)
        for (i = 0; i < vlen; ++i)
            newvar[i] += cds[j]->bfact_c * cds[j]->prvar[i];

    for (i = 0; i < vlen; ++i)
        newvar[i] *= fact;
}


void
InvGammaFitEvalsBfact(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL;
    int             count, newlen;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        newlen = vlen - 3;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            qsort(newvar, vlen, sizeof(double), dblcmp_rev);
            chi2 = invgamma_fit(newvar, newlen, &b, &c, &logL);

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarBfactOcc(cdsA, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVarBfact(cdsA->cds, newvar, vlen, cnum, variance, b, c);

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (cdsA->algo->bfact == 2)
                CalcBfactC(cdsA);

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        if (cdsA->algo->alignment == 1)
            InvGammaAdjustVarBfactOcc(cdsA, variance, vlen, cnum, variance, b, c);
        else
            InvGammaAdjustVarBfact(cdsA->cds, variance, vlen, cnum, variance, b, c);
    }
    else if (cdsA->algo->covweight != 0)
    {
        printf("\n  ERROR:  B-factor weighting cannot be used yet with covariance matrix weighting -c \n");
        Usage(0);
        exit(EXIT_FAILURE);
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


/* This one includes the smallest distinct eigenvalue (though omitting it from the
   first round of fitting) */
void
InvGammaFitEvals3(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL, harmave, mode;
    int             i, j, count, newlen, df;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
/*         b = cdsA->stats->hierarch_p1; */
/*         c = cdsA->stats->hierarch_p2; */
        b = 0.01;
        c = 1.0;
/*         newlen = vlen - 1; */

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

/*             if (count > 2) */
                newlen = vlen;

            qsort(newvar, vlen, sizeof(double), dblcmp);
            chi2 = invgamma_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            if (2*c + 2 > 100 * 3 * cnum)
            {
                harmave = HarmonicAve(newvar + vlen - newlen, newlen);

                for (i = 0; i < vlen; ++i)
                    variance[i] = harmave;

                return;
            }

            if (cdsA->algo->alignment == 1)
            {
                for (i = 0; i < vlen; ++i)
                {
                    df = 0;
                    for (j = 0; j < cnum; ++j)
                        df += cdsA->cds[j]->o[i];

                    df *= 3;

                    newvar[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
                }
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
            }

            /* newvar[findmin(variance, vlen)] = 2.0*b / (nd + 2.0*(1.0 + c)); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            count++;

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count++, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }
    }
    else if (cdsA->algo->covweight != 0)
    {
        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        evecs = cdsA->tmpmatKK2;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        b = c = 0.0;

/*          for (i = 0; i < vlen; ++i) */
/*          newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c)); */

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

/*             if (count > 1) */
/*             { */
/*                 newlen = vlen; */
/*  */
/*                 mode = b / (c+1.0); */
/*  */
/*                 for (i = 0; i < vlen - newlen; ++i) */
/*                     variance[i] = mode; */
/*             } */

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            chi2 = invgamma_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            for (i = vlen - newlen; i < newlen; ++i)
                printf("%3d %e\n", i, newvar[i]);

            if (cdsA->algo->verbose != 0)
            {
                mode = b / (c+1.0);
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, mode, logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            ++count;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        InvGammaAdjustCov(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];
    }

    if (cdsA->algo->alignment == 1)
    {
        for (i = 0; i < vlen; ++i)
        {
            df = 0;
            for (j = 0; j < cnum; ++j)
                df += cdsA->cds[j]->o[i];

            df *= 3;

            variance[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
        }
    }
    else
    {
        for (i = 0; i < vlen; ++i)
            variance[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaBayesFitEvals3(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL, harmave, mode;
    int             i, j, count, newlen, df;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;

        newlen = vlen - 1;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            if (count > 2)
                newlen = vlen;

            qsort(newvar, vlen, sizeof(double), dblcmp);
            chi2 = invgamma_bayes_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            if (2*c + 2 > 100 * 3 * cnum)
            {
                harmave = HarmonicAve(newvar + vlen - newlen, newlen);

                for (i = 0; i < vlen; ++i)
                    variance[i] = harmave;

                return;
            }

            if (cdsA->algo->alignment == 1)
            {
                for (i = 0; i < vlen; ++i)
                {
                    df = 0;
                    for (j = 0; j < cnum; ++j)
                        df += cdsA->cds[j]->o[i];

                    df *= 3;

                    newvar[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
                }
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
            }

            /* newvar[findmin(variance, vlen)] = 2.0*b / (nd + 2.0*(1.0 + c)); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            count++;

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count++, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }
    }
    else if (cdsA->algo->covweight != 0)
    {
        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        evecs = cdsA->tmpmatKK2;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        b = c = 0.0;

/*          for (i = 0; i < vlen; ++i) */
/*              newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c)); */

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

/*             if (count > 1) */
/*             { */
/*                 newlen = vlen; */
/*  */
/*                 mode = b / (c+1.0); */
/*  */
/*                 for (i = 0; i < vlen - newlen; ++i) */
/*                     variance[i] = mode; */
/*             } */

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            chi2 = invgamma_bayes_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            for (i = vlen - newlen; i < newlen; ++i)
                printf("%3d %e\n", i, newvar[i]);

            if (cdsA->algo->verbose != 0)
            {
                mode = b / (c+1.0);
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, mode, logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            ++count;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        InvGammaAdjustCov(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];
    }

    if (cdsA->algo->alignment == 1)
    {
        for (i = 0; i < vlen; ++i)
        {
            df = 0;
            for (j = 0; j < cnum; ++j)
                df += cdsA->cds[j]->o[i];

            df *= 3;

            variance[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
        }
    }
    else
    {
        for (i = 0; i < vlen; ++i)
            variance[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}





/* b = c in inverse gamma fit */
void
InvGammaFitEvalsEq(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL, harmave, mode;
    int             i, j, count, newlen, df;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;

        if (!isfinite(b) || !isfinite(c))
        {
            printf("\n ERROR01: b(%e) or c(%e) parameter in InvGammaFitVarsEq() not finite\n",
                   b, c);
            fflush(NULL);
            exit(EXIT_FAILURE);
        }

        newlen = vlen - 1;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            if (count > 2)
                newlen = vlen;

            qsort(newvar, vlen, sizeof(double), dblcmp);

            if (count == 0 && cdsA->algo->rounds < 8)
                chi2 = invgamma_eq_bc_fit(newvar + vlen - newlen, newlen, &b, &c, &logL, 0);
            else
                chi2 = invgamma_eq_bc_fit(newvar + vlen - newlen, newlen, &b, &c, &logL, 1);

            cdsA->stats->hierarch_p1 = b;
            cdsA->stats->hierarch_p2 = c;

            if (2*c + 2 > 100 * 3 * cnum)
            {
                harmave = HarmonicAve(newvar + vlen - newlen, newlen);

                for (i = 0; i < vlen; ++i)
                    variance[i] = harmave;

                return;
            }

            if (cdsA->algo->alignment == 1)
            {
                for (i = 0; i < vlen; ++i)
                {
                    df = 0;
                    for (j = 0; j < cnum; ++j)
                        df += cdsA->cds[j]->o[i];

                    df *= 3;

                    newvar[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
                }
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
            }

            /* newvar[findmin(variance, vlen)] = 2.0*b / (nd + 2.0*(1.0 + c)); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            count++;

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count++, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }
    }
    else if (cdsA->algo->covweight != 0)
    {
        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        evecs = cdsA->tmpmatKK2;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        b = c = 0.0;

/*          for (i = 0; i < vlen; ++i) */
/*          newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c)); */

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            if (count > 2)
                newlen = vlen;

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            chi2 = invgamma_eq_bc_fit(newvar + vlen - newlen, newlen, &b, &c, &logL, 0);

            for (i = vlen - newlen; i < newlen; ++i)
                printf("%3d %e\n", i, newvar[i]);

            if (cdsA->algo->verbose != 0)
            {
                mode = b / (c+1.0);
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count, b, c, mode, logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            ++count;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        InvGammaAdjustCov(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];
    }

    if (cdsA->algo->alignment == 1)
    {
        for (i = 0; i < vlen; ++i)
        {
            df = 0;
            for (j = 0; j < cnum; ++j)
                df += cdsA->cds[j]->o[i];

            df *= 3;

            variance[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
        }
    }
    else
    {
        for (i = 0; i < vlen; ++i)
            variance[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaFitEvals2(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = DBL_MAX, logL, harmave;
    int             i, j, count, newlen, df;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
        newlen = vlen - 3;

        memcpy(newvar, variance, vlen * sizeof(double));
        qsort(newvar, vlen, sizeof(double), dblcmp);
        chi2 = invgamma_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

        if (2*c + 2 > 100 * 3 * cnum)
        {
            harmave = HarmonicAve(newvar + vlen - newlen, newlen);

            for (i = 0; i < vlen; ++i)
                variance[i] = harmave;

            return;
        }

        if (cdsA->algo->alignment == 1)
        {
            for (i = 0; i < vlen; ++i)
            {
                df = 0;
                for (j = 0; j < cnum; ++j)
                    df += cdsA->cds[j]->o[i];

                df *= 3;

                newvar[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
            }
        }
        else
        {
            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
        }

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            qsort(newvar, vlen, sizeof(double), dblcmp);
            chi2 = invgamma_fit(newvar, vlen, &b, &c, &logL);

            if (2*c + 2 > 100 * 3 * cnum)
            {
                harmave = HarmonicAve(newvar, vlen);

                for (i = 0; i < vlen; ++i)
                    variance[i] = harmave;

                return;
            }

            if (cdsA->algo->alignment == 1)
            {
                for (i = 0; i < vlen; ++i)
                {
                    df = 0;
                    for (j = 0; j < cnum; ++j)
                        df += cdsA->cds[j]->o[i];

                    df *= 3;

                    newvar[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
                }
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
            }

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                       count++, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
                fflush(NULL);
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e\n",
                   count++, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }
    }
    else if (cdsA->algo->covweight != 0)
    {
        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        evecs = cdsA->tmpmatKK2;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;
/*         b = c = 0.0; */

        for (i = 0; i < vlen; ++i)
            newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        count = 0;
        do
        {
            ++count;
            oldb = b;
            oldc = c;

            chi2 = invgamma_fit(newvar + vlen - newlen, newlen, &b, &c, &logL);

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

/*             for (i = 0; i < newlen; ++i) */
/*                 printf("\n%3d %e", i, newvar[i]); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        InvGammaAdjustCov(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];
    }

    if (cdsA->algo->alignment == 1)
    {
        for (i = 0; i < vlen; ++i)
        {
            df = 0;
            for (j = 0; j < cnum; ++j)
                df += cdsA->cds[j]->o[i];

            df *= 3;

            variance[i] = (df*variance[i] + 2.0*b) / (df + 2.0*(1.0 + c));
        }
    }
    else
    {
        for (i = 0; i < vlen; ++i)
            variance[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGamma1FitEvals(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, chi2 = DBL_MAX, logL, nullp = 1.0;
    int             i, j, count, newlen, df;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (vlen - 3 < nd - 6)
        newlen = vlen - 3;
    else
        newlen = nd - 6;

    if (cdsA->algo->varweight != 0)
    {
        b = cdsA->stats->hierarch_p1;
        /* newlen = vlen - 3; */
        newlen = vlen;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;

            qsort(newvar, vlen, sizeof(double), dblcmp);

            chi2 = invgamma1_fit(newvar + vlen - newlen, newlen, &b, &nullp, &logL);

            if (cdsA->algo->alignment == 1)
            {
                for (i = 0; i < vlen; ++i)
                {
                    df = 0;
                    for (j = 0; j < cnum; ++j)
                        df += cdsA->cds[j]->o[i];

                    df *= 3;

                    newvar[i] = (df*variance[i] + 2.0*b) / (df + 4.0);
                }
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 4.0);
            }

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count++, b, b / 2.0, logL, chi2);
                fflush(NULL);
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                   count++, b, b / 2.0, logL, chi2);
            fflush(NULL);
        }
    }
    else if (cdsA->algo->covweight != 0)
    {
        evecs = cdsA->tmpmatKK2;

        eigenvalsym((const double **) cdsA->CovMat, variance, evecs, vlen);

        /* RevVecIp(variance, vlen); */

        for (i = 0; i < vlen - newlen; ++i)
            variance[i] = 0.0;

        b = cdsA->stats->hierarch_p1;

        for (i = 0; i < vlen; ++i)
            newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 4.0);

        count = 0;
        do
        {
            ++count;
            oldb = b;

            chi2 = invgamma1_fit(newvar + vlen - newlen, newlen, &b, &nullp, &logL);

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 4.0);

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        InvGammaAdjustCov(cdsA, b, 1.0);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];
    }

    if (cdsA->algo->alignment == 1)
    {
        for (i = 0; i < vlen; ++i)
        {
            df = 0;
            for (j = 0; j < cnum; ++j)
                df += cdsA->cds[j]->o[i];

            df *= 3;

            variance[i] = (df*variance[i] + 2.0*b) / (df + 4.0);
        }
    }
    else
    {
        for (i = 0; i < vlen; ++i)
            variance[i] = (nd*variance[i] + 2.0*b) / (nd + 4.0);
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = 1.0;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaFitVarsND(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2, logL;
    int             i, count, newlen;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->covweight != 0)
    {
        double    **evecs = MatAlloc(vlen, vlen);

        if (vlen - 1 < nd - 3)
            newlen = vlen - 1;
        else
            newlen = nd - 3;

        eigensym((const double **) cdsA->CovMat, variance, evecs, vlen);
        RevVecIp(variance, vlen);

        for (i = newlen; i < vlen; ++i)
            variance[i] = 0.0;

        memcpy(newvar, variance, vlen * sizeof(double));
        count = 0;
        do
        {
            ++count;
            oldb = b;
            oldc = c;

            chi2 = invgamma_fit(newvar, newlen, &b, &c, &logL);

            for (i = 0; i < newlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count++, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 100)
            {
                printf("\n WARNING01: Failed to converge in InvGammaFitVars(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        InvGammaAdjustCovND(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];

        MatDestroy(&evecs);
    }
    else
    {
        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            chi2 = invgamma_fit(newvar, vlen, &b, &c, &logL);

            for (i = 0; i < vlen; ++i)
                newvar[i] = (variance[i] + 2.0*b) / (3.0 + 2.0*c);

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count++, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (iterate == 0)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf(">>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                   count++, b, c, b / (c+1.0), logL, chi2);
            fflush(NULL);
        }

        memcpy(variance, newvar, vlen * sizeof(double));
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


/* Fit a gamma distribution by maximum likelihood.
   Uses Newton-Raphson. */
/* void */
/* InvGammaFitVars(CdsArray *cdsA, int iterate) */
/* { */
/*     double         *newvar = NULL; */
/*     double         *variance = cdsA->var; */
/*     double          precision = cdsA->algo->precision; */
/*     const int       vlen = cdsA->vlen, cnum = cdsA->cnum; */
/*     double          nd, oldb, oldc, harmave, b, c, chi2, logL; */
/*     int             i, count; */
/*  */
/*     newvar = malloc(vlen * sizeof(double)); */
/*     nd = 3.0 * cnum; */
/*     oldb = oldc = DBL_MAX; */
/*  */
/*     if (cdsA->algo->covweight != 0) */
/*     { */
/*         for (i = 0; i < vlen; ++i) */
/*             newvar[i] = variance[i] = cdsA->CovMat[i][i]; */
/*     } */
/*     else */
/*      memcpy(newvar, variance, vlen * sizeof(double)); */
/*  */
/*  count = 0; */
/*  do */
/*  { */
/*      oldb = b; */
/*      oldc = c; */
/*  */
/*      chi2 = invgamma_fit(newvar, vlen, &b, &c, &logL); */
/*  */
/*      printf("\n>>>>> %3d b:%-10.5f c:%-10.5f mode:%-10.5f logL:% -12.6f chi2:%-10.5f", */
/*             ++count, b, c, b / (c+1.0), logL, chi2); */
/*      fflush(NULL); */
/*  */
/*      if (c > FLT_MAX) */
/*      { */
/*          harmave = 0.0; */
/*          for (i = 0; i < vlen; ++i) */
/*          { */
/*              if(variance[i] == 0.0) */
/*                  continue; */
/*              else */
/*                  harmave += (1.0 / variance[i]); */
/*          } */
/*          harmave = vlen / harmave; */
/*          memsetd(newvar, harmave, vlen); */
/*          break; */
/*      } */
/*      else if (b < DBL_EPSILON) */
/*      { */
/*          for (i = 0; i < vlen; ++i) */
/*              newvar[i] = (nd*variance[i]) / (nd + 2.0*(1.0 + c)); */
/*          break; */
/*      } */
/*      else if (2.0 + 2.0 * c > 20.0 * nd) */
/*      { */
/*          for (i = 0; i < vlen; ++i) */
/*              newvar[i] = b/c; */
/*          break; */
/*      } */
/*      else */
/*      { */
/*          for (i = 0; i < vlen; ++i) */
/*              newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c)); */
/*      } */
/*  */
/*      if (iterate == 0) */
/*          break; */
/*  } */
/*  while(fabs(oldb - b) > fabs(b*precision) && */
/*        fabs(oldc - c) > fabs(c*precision)); */
/*  */
/*  memcpy(variance, newvar, vlen * sizeof(double)); */
/*  */
/*     cdsA->stats->hierarch_p1 = b; */
/*     cdsA->stats->hierarch_p2 = c; */
/*     cdsA->stats->hierarch_chi2 = chi2; */
/*  */
/*     free(newvar); */
/* } */


/* Fit of the variances/eigenvalues to an inverse gamma distribution with
   a constrained, minimum c shape parameter.
   min c = 1 guarantees that the distribution has a finite mean
   min c = 2 -> finite variance
   min c = 3 -> finite skewness
   min c = 4 -> finite kurtosis */
void
InvGammaFitVars_minc(CdsArray *cdsA, const double minc, int iterate)
{
    double         *newvar = NULL;
    double         *evals = cdsA->var;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, harmave, b, c, chi2, logL, mode;
    int             i, count, newlen, delay;
    int             maxcount = 300;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;
    delay = INT_MAX;

    if (cdsA->algo->covweight != 0)
    {
        double    **evecs = MatAlloc(vlen, vlen);

        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        eigensym((const double **) cdsA->CovMat, evals, evecs, vlen);
        RevVecIp(evals, vlen);

        for (i = newlen; i < vlen; ++i)
            evals[i] = 0.0;

        memcpy(newvar, evals, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            if (count < delay)
                chi2 = invgamma_fit(newvar, newlen, &b, &c, &logL);
            else
                chi2 = invgamma_fit(newvar, vlen, &b, &c, &logL);

            for (i = 0; i < newlen; ++i)
                newvar[i] = (nd*evals[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            mode = b / (c+1.0); /* the mode of an inv gamma dist; the ML estimate of missing data */
            for (i = newlen; i < vlen; ++i)
                newvar[i] = mode;

/*             printf("\n\n count: %d", count); */
/*             for (i = 0; i < vlen; ++i) */
/*                 printf("\n%3d %8.3e", i, newvar[i]); */

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > maxcount)
            {
                printf("\n WARNING03: Failed to converge in InvGammaFitVars_minc(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (c < minc)
        {
            memcpy(newvar, evals, vlen * sizeof(double));

            if (vlen - 3 < nd - 6)
                newlen = vlen - 3;
            else
                newlen = nd - 6;

            c = minc;
            count = 0;
            do
            {
                oldb = b;

                if (count < delay)
                    b = minc * HarmonicAve(newvar, newlen);
                else
                    b = minc * HarmonicAve(newvar, vlen);

                for (i = 0; i < newlen; ++i)
                    newvar[i] = (nd*evals[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

                mode = b / (c+1.0); /* the mode of an inv gamma dist; the ML estimate of missing data */
                for (i = newlen; i < vlen; ++i)
                    newvar[i] = mode;

                if (cdsA->algo->verbose != 0)
                {
                    printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                           count, b, c, b / (c+1.0), logL, chi2);
                    fflush(NULL);
                }

                if (count > maxcount)
                {
                    printf("\n WARNING04: Failed to converge in InvGammaFitVars_minc(), round %d\n    ",
                           cdsA->algo->rounds);
                    fflush(NULL);
                    break;
                }

                if (iterate == 0)
                    break;

                ++count;
            }
            while(fabs(oldb - b) > fabs(b*precision));

            chi2 = chi_sqr_adapt(newvar, newlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
        }

/*      printf("\n\n count: %d", count); */
/*      for (i = 0; i < vlen; ++i) */
/*          printf("\n%3d %8.3e", i, newvar[i]); */

/*         RevVecIp(evals, vlen); */
/*         EigenReconSym(cdsA->CovMat, (const double **) evecs, (const double *) evals, vlen); */

        InvGammaAdjustCov(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            cdsA->var[i] = cdsA->CovMat[i][i];

        MatDestroy(&evecs);
    }
    else
    {
        memcpy(newvar, evals, vlen * sizeof(double));
        newlen = vlen - 3;

        count = 0;
        do
        {
            oldb = b;
            oldc = c;

            qsort(newvar, vlen, sizeof(double), dblcmp_rev);
/*             printf("\n\n count: %d", count); */
/*             for (i = 0; i < vlen; ++i) */
/*                 printf("\n%3d %8.3e", i, newvar[i]); */

            if (count < delay)
                chi2 = invgamma_fit(newvar, newlen, &b, &c, &logL);
            else
                chi2 = invgamma_fit(newvar, vlen, &b, &c, &logL);

            for (i = newlen; i < vlen; ++i)
                newvar[i] = b / (c+1.0);

/*             printf("\n\n count: %d", count); */
/*             for (i = 0; i < vlen; ++i) */
/*                 printf("\n%3d %8.3e", i, newvar[i]); */

            if (c > FLT_MAX)
            {
                harmave = HarmonicAve(newvar, vlen);
                memsetd(newvar, harmave, vlen);
                break;
            }
            else if (b < DBL_EPSILON)
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*evals[i]) / (nd + 2.0*(1.0 + c));
                break;
            }
            else if (2.0 + 2.0 * c > 20.0 * nd) /* all are equal to the harmonic average */
            {
                harmave = HarmonicAve(newvar, vlen);
                for (i = 0; i < vlen; ++i)
                    newvar[i] = harmave;
                break;
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*evals[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
            }

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > maxcount)
            {
                printf("\n WARNING04: Failed to converge in InvGammaFitVars_minc(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

/*             double trace1 = 0.0, trace2 = 0.0; */
/*             for (i = 0; i < vlen; ++i) */
/*             { */
/*                 trace1 += newvar[i]; */
/*                 trace2 += evals[i]; */
/*             } */
/*  */
/*          printf("\n trace 1: %f   trace 2: %f", */
/*                 trace1, trace2); */
/*          fflush(NULL); */

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(oldb - b) > fabs(b*precision) &&
              fabs(oldc - c) > fabs(c*precision));

        if (c < minc)
        {
            c = minc;
            memcpy(newvar, evals, vlen * sizeof(double));
            newlen = vlen - 3;

            count = 0;
            do
            {
                oldb = b;

/*                 if (count >= delay) */
/*                     newlen = vlen; */

                qsort(newvar, vlen, sizeof(double), dblcmp);

/*                 if (count >= delay) */
/*                     for (i = 0; i < vlen - newlen; ++i) */
/*                         newvar[i] = b / (c+1.0); */

                if (count < delay)
                    b = minc * HarmonicAve(newvar, newlen);
                else
                    b = minc * HarmonicAve(newvar, vlen);

                for (i = newlen; i < vlen; ++i)
                    newvar[i] = b / (c+1.0);

                if (b < DBL_EPSILON)
                {
                    for (i = 0; i < vlen; ++i)
                        newvar[i] = (nd*evals[i]) / (nd + 2.0*(1.0 + c));
                    break;
                }
                else
                {
                    for (i = 0; i < vlen; ++i)
                        newvar[i] = (nd*evals[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
                }

                if (cdsA->algo->verbose != 0)
                {
                    printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                           count, b, c, b / (c+1.0), logL, chi2);
                    fflush(NULL);
                }

                if (count > maxcount)
                {
                    printf("\n WARNING04: Failed to converge in InvGammaFitVars_minc(), round %d\n    ",
                           cdsA->algo->rounds);
                    fflush(NULL);
                    break;
                }

                if (iterate == 0)
                    break;

                ++count;
            }
            while(fabs(oldb - b) > fabs(b*precision));
        }

        for (i = 0; i < vlen; ++i)
            cdsA->var[i] = (nd*evals[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        chi2 = chi_sqr_adapt(evals, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
    }

/*  for (i = 0; i < vlen; ++i) */
/*      printf("\n%3d %e", i, newvar[i]); */

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


/*
Straight-up EM fit, for marginal student-t, like
Mechelke and Habeck BMC Bioinformatics 2010, 11:363
2010-10-31
*/
void
InvGammaEMFixedC(CdsArray *cdsA, const double c, int iterate)
{
    double         *newvar = NULL;
    double         *variance = NULL;
    double          precision = FLT_EPSILON; // cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b = 0.0, chi2 = 0.0, logL;
    int             count;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight > 0)
    {
        variance = cdsA->var;
        b = cdsA->stats->hierarch_p1;

        memcpy(newvar, variance, vlen * sizeof(double));

        if (cdsA->algo->verbose != 0)
            printf("\n0>>>>>>>>>>>>>");

        count = 0;
        do
        {
            oldb = b;

            b = HarmonicAve(newvar, vlen);
            //b = HarmonicAveBayes(newvar, vlen, 1.0);
            //printf("\nb:%g",b);
            //fflush(NULL);

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVar(newvar, vlen, cnum, variance, b, c);

            if (cdsA->algo->verbose != 0)
            {
                printf("\n>>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaBayesFixedCFitEvals(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(b - oldb) > fabs(b*precision));

		if (cdsA->algo->alignment == 1)
			InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
		else
			InvGammaAdjustVar(newvar, vlen, cnum, variance, b, c);

        chi2 = chi_sqr_adapt(variance, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
    }

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


/* DLT 2008-03-28 new */
void
InvGammaEMFixedCFitEvals(CdsArray *cdsA, const double c, int iterate)
{
    double         *newvar = NULL;
    double         *evals = NULL;
    double         *variance = NULL;
    double        **evecs = NULL;
    int            *missi = NULL;
    double          precision = FLT_EPSILON; //cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b = 0.0, chi2 = 0.0, logL, xn1, expinvx, gt;
    int             i, j, count, newlen, missing;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight > 0)
    {
        variance = cdsA->var;
        b = cdsA->stats->hierarch_p1;
        missing = 4;
        missi = malloc(missing * sizeof(int));

        memcpy(newvar, variance, vlen * sizeof(double));

        if (cdsA->algo->verbose != 0)
            printf("\n0>>>>>>>>>>>>>");

        count = 0;
        do
        {
            oldb = b;

            /* qsort-dblcmp sorts small to big */
            qsort(newvar, vlen, sizeof(double), dblcmp);

            xn1 = newvar[missing];
/*             if (cdsA->algo->bayes == 0) */
            invgamma_fixed_c_EM_fit(newvar, vlen, missing, &b, c, &logL);
/*             else if (cdsA->algo->bayes == 1) */
/*                 invgamma_fixed_c_EM_fit_bayes(newvar, vlen, missing, &b, c, &logL); */

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVar(newvar, vlen, cnum, variance, b, c);

            if (cdsA->algo->verbose != 0)
            {
                printf("\n>>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaBayesFixedCFitEvals(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(b - oldb) > fabs(b*precision));

        expinvx = 1.0 / ExpInvXn(b, c, xn1);
        
/* 
        printf("\n%g", 1.0 / expinvx);
        printf("\n%g", 1 / xn1);
        printf("\n%g\n", 2*(1/xn1 - 1/newvar[missing+1]) + 1/xn1);
 */

        gt = 0.0;
        for (j = 0; j < missing; ++j)
        {
            missi[j] = findsmallest_gt(variance, vlen, gt);

            if (cdsA->algo->verbose != 0)
            {
                printf("\n>>>>>>>>>>>>>> %d", j);
                printf("\n>>>>>>>>>>>>>>   pre-var       xn1   expinvx");
                printf("\n1>>>>>>>>>>>>> %8.3e %8.3e %8.3e\n", variance[missi[j]], xn1, expinvx);
                fflush(NULL);
            }

            gt = variance[missi[j]];
            /* this is because the inverse of the variance is always used in other 
               calculations/maximizations, yet I store it as the variance (uninverted) */
        }

        for (j = 0; j < missing; ++j)
            variance[missi[j]] = expinvx;

        free(missi);
        chi2 = chi_sqr_adapt(variance, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
    }
    else if (cdsA->algo->covweight > 0)
    {
        evecs = cdsA->tmpmatKK2;
        evals = cdsA->var;

/*         if (vlen - 3 < nd - 6) */
/*             newlen = vlen - 3; */
/*         else */
/*             newlen = nd - 6; */

        if (vlen - 4 < nd - 4)
            newlen = vlen - 4;
        else
            newlen = nd - 4;

//newlen = vlen-3;
        missing = vlen - newlen;

        eigensym((const double **) cdsA->CovMat, evals, evecs, vlen);
        /* eigensym evals are small to large */    

//VecPrint(evals, vlen);
        //printf("missing:%d\n\n", missing);

        for (i = 0; i < missing; ++i)
            evals[i] = 0.0;

        //VecPrint(evals, vlen);

        b = cdsA->stats->hierarch_p1;

        memcpy(newvar, evals, vlen * sizeof(double));

        if (cdsA->algo->verbose != 0)
            printf("\n0>>>>>>>>>>>>>");

        count = 0;
        do
        {
            oldb = b;

            qsort(newvar, vlen, sizeof(double), dblcmp);

            xn1 = newvar[missing];
            //chi2 = invgamma_fixed_c_EM_fit(newvar, vlen, missing, &b, c, &logL);

/*             if (cdsA->algo->bayes == 0) */
                chi2 = invgamma_fixed_c_EM_fit(newvar, vlen, missing, &b, c, &logL);
/*             else if (cdsA->algo->bayes == 1) */
/*                 chi2 = invgamma_fixed_c_EM_fit_bayes(newvar, vlen, missing, &b, c, &logL); */

            for (i = missing; i < vlen; ++i)
                newvar[i] = (nd*evals[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            if (cdsA->algo->verbose != 0)
            {
                printf("\n>>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaBayesFixedCFitEvals(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf("\n>>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        InvGammaAdjustVar(evals+missing, vlen-missing, cnum, evals+missing, b, c);

        xn1 = evals[missing];

        if (cdsA->algo->verbose != 0)
        {
            printf("\n1>>>> %8.3e %8.3e", evals[0], xn1);
            fflush(NULL);
        }

        expinvx = 1.0 / ExpInvXn(b, c, xn1);

        for (i = 0; i < missing; ++i)
            evals[i] = expinvx;

        if (cdsA->algo->verbose != 0)
        {
            printf("\n2>>>> %8.3e %8.3e\n", xn1, evals[0]);
            fflush(NULL);
        }

        //chi2 = chi_sqr_adapt(evals, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
        //chi2 = chi_sqr_adapt(newvar, newlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);

        EigenReconSym(cdsA->CovMat, (const double **) evecs, evals, vlen);

        for (i = 0; i < vlen; ++i)
            evals[i] = cdsA->CovMat[i][i];
    }

/*     for (i = 0; i < vlen; ++i) */
/*        printf("\n%3d %e", i, evals[i]); */

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaMLFixedCFitEvals(CdsArray *cdsA, const double c, int iterate)
{
    double         *newvar = NULL;
    double         *evals = NULL;
    double         *variance = NULL;
    double        **evecs = NULL;
    double          precision = FLT_EPSILON; //cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b = 0.0, chi2 = 0.0, logL, xn1, expinvx;
    int             i, count, newlen, missing, smallest;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        variance = cdsA->var;
        b = cdsA->stats->hierarch_p1;
        missing = 1;

        memcpy(newvar, variance, vlen * sizeof(double));

        if (cdsA->algo->verbose != 0)
            printf("\n0>>>>>>>>>>>>>");

        count = 0;
        do
        {
            oldb = b;

            /* qsort-dblcmp sorts small to big */
            qsort(newvar, vlen, sizeof(double), dblcmp);

            xn1 = newvar[1];
/*             if (cdsA->algo->bayes == 0) */
                invgamma_fixed_c_ML_fit(newvar, vlen, missing, &b, c, &logL);
/*             else if (cdsA->algo->bayes == 1) */
/*             { */
/*                 printf("\nXXXXXXXXXXXXXXXXXXXXXXXXX\n\n"); */
/*                 exit(0); */
/*                 chi2 = invgamma_fixed_c_ML_fit_bayes(newvar, vlen, missing, &b, c, &logL); */
/*             } */

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVar(newvar, vlen, cnum, variance, b, c);

            if (cdsA->algo->verbose != 0)
            {
                printf("\n>>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaBayesFixedCFitEvals(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(b - oldb) > fabs(b*precision));

        InvGammaAdjustVar(variance, vlen, cnum, variance, b, c);

        smallest = findsmallest(variance, vlen);
        expinvx = 1.0 / ExpInvXn(b, c, xn1);

        if (cdsA->algo->verbose != 0)
        {
            printf("\n>>>>>>>>>>>>>>   pre-var       xn1   expinvx");
            printf("\n1>>>>>>>>>>>>> %8.3e %8.3e %8.3e\n", variance[smallest], xn1, expinvx);
            fflush(NULL);
        }

        variance[smallest] = expinvx;
        /* this is because the inverse of the variance is always used in other 
           calculations/maximizations, yet I store it as the variance (uninverted) */

        chi2 = chi_sqr_adapt(variance, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
    }
    else if (cdsA->algo->covweight != 0)
    {
        evecs = cdsA->tmpmatKK2;
        evals = cdsA->var;

/*         if (vlen - 3 < nd - 6) */
/*             newlen = vlen - 3; */
/*         else */
/*             newlen = nd - 6; */

        if (vlen - 4 < nd - 3)
            newlen = vlen - 4;
        else
            newlen = nd - 3;

//newlen = vlen-3;
        missing = vlen - newlen;

        eigensym((const double **) cdsA->CovMat, evals, evecs, vlen);
        /* eigensym evals are small to large */    

//VecPrint(evals, vlen);
        //printf("missing:%d\n\n", missing);

        for (i = 0; i < missing; ++i)
            evals[i] = 0.0;

        //VecPrint(evals, vlen);

        b = cdsA->stats->hierarch_p1;

        memcpy(newvar, evals, vlen * sizeof(double));

        if (cdsA->algo->verbose != 0)
            printf("\n0>>>>>>>>>>>>>");

        count = 0;
        do
        {
            oldb = b;

            qsort(newvar, vlen, sizeof(double), dblcmp);

            xn1 = newvar[missing];
            //chi2 = invgamma_fixed_c_EM_fit(newvar, vlen, missing, &b, c, &logL);

/*             if (cdsA->algo->bayes == 0) */
                chi2 = invgamma_fixed_c_ML_fit(newvar, vlen, missing, &b, c, &logL);
/*             else if (cdsA->algo->bayes == 1) */
/*             { */
/*                 printf("\nXXXXXXXXXXXXXXXXXXXXXXXXX\n\n"); */
/*                 exit(0); */
/*                 chi2 = invgamma_fixed_c_ML_fit_bayes(newvar, vlen, missing, &b, c, &logL); */
/*             } */

            for (i = missing; i < vlen; ++i)
                newvar[i] = (nd*evals[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            if (cdsA->algo->verbose != 0)
            {
                printf("\n>>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaBayesFixedCFitEvals(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf("\n>>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        InvGammaAdjustVar(evals+missing, vlen-missing, cnum, evals+missing, b, c);

        xn1 = evals[missing];

        if (cdsA->algo->verbose != 0)
        {
            printf("\n1>>>> %8.3e %8.3e", evals[0], xn1);
            fflush(NULL);
        }

        expinvx = 1.0 / ExpInvXn(b, c, xn1);

        for (i = 0; i < missing; ++i)
            evals[i] = expinvx;

        if (cdsA->algo->verbose != 0)
        {
            printf("\n2>>>> %8.3e %8.3e\n", xn1, evals[0]);
            fflush(NULL);
        }

        //chi2 = chi_sqr_adapt(evals, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
        //chi2 = chi_sqr_adapt(newvar, newlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);

        EigenReconSym(cdsA->CovMat, (const double **) evecs, evals, vlen);

        for (i = 0; i < vlen; ++i)
            evals[i] = cdsA->CovMat[i][i];
    }

/*     for (i = 0; i < vlen; ++i) */
/*        printf("\n%3d %e", i, evals[i]); */

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}



void
InvGammaMLFitEvals(CdsArray *cdsA, int iterate)
{
    double         *newvar = NULL;
    double         *evals = NULL;
    double         *variance = NULL;
    double        **evecs = NULL;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b, c, chi2 = 0.0, logL, xn1, expinvx;
    int             i, count, newlen, missing, smallest;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->varweight != 0)
    {
        variance = cdsA->var;
        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;

        missing = 1;

        memcpy(newvar, variance, vlen * sizeof(double));

        if (cdsA->algo->verbose != 0)
            printf("\n0>>>>>>>>>>>>>");

        count = 0;
        do
        {
            oldb = b;

            if (cdsA->algo->verbose != 0)
            {
                printf("\n>>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (cdsA->algo->alignment == 1)
                InvGammaAdjustVarOcc(cdsA->cds, newvar, vlen, cnum, variance, b, c);
            else
                InvGammaAdjustVar(newvar, vlen, cnum, variance, b, c);

            /* qsort-dblcmp sorts small to big */
            qsort(newvar, vlen, sizeof(double), dblcmp);
//            for (i = 0; i < vlen; ++i)
//                printf("\n%3d %e", i, newvar[i]);
            xn1 = newvar[1];
            invgamma_EMsmall_fit(newvar, vlen, missing, &b, &c, &logL);

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaMLFitEvals(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        InvGammaAdjustVar(variance, vlen, cnum, variance, b, c);

        smallest = findsmallest(variance, vlen);

        if (cdsA->algo->verbose != 0)
        {
            printf("\n1>>>> %8.3e %8.3e", variance[smallest], xn1);
            fflush(NULL);
        }

        //variance[smallest] = ExpXn(b, c, xn1);
        variance[smallest] = 1.0 / ExpInvXn(b, c, xn1);
        /* this is because the inverse of the variance is always used in other 
           calculations/maximizations, yet I store it as the variance (uninverted) */
        //printf("\n-->-->-->-->--> %8.3e %8.3e\n", variance[smallest], ExpXn(b, c, xn1));

        if (cdsA->algo->verbose != 0)
        {
            printf("\n2>>>> %8.3e %8.3e\n", xn1, variance[smallest]);
            fflush(NULL);
        }

        chi2 = chi_sqr_adapt(variance, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
    }
    else if (cdsA->algo->covweight != 0)
    {
        evecs = cdsA->tmpmatKK2;
        evals = cdsA->var;

        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        missing = vlen - newlen;

        eigensym((const double **) cdsA->CovMat, evals, evecs, vlen);
        /* eigensym evals are small to large */    

        for (i = 0; i < missing; ++i)
            evals[i] = 0.0;

        b = cdsA->stats->hierarch_p1;
        c = cdsA->stats->hierarch_p2;

        memcpy(newvar, evals, vlen * sizeof(double));

        if (cdsA->algo->verbose != 0)
            printf("\n0>>>>>>>>>>>>>");

        count = 0;
        do
        {
            oldb = b;

            InvGammaAdjustVar(newvar+missing, vlen-missing, cnum, evals+missing, b, c);

            qsort(newvar, vlen, sizeof(double), dblcmp);

            if (cdsA->algo->verbose != 0)
            {
                printf("\n>>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            xn1 = newvar[missing];
            chi2 = invgamma_EMsmall_fit(newvar, vlen, missing, &b, &c, &logL);

//            for (i = missing; i < vlen; ++i)
//                newvar[i] = (nd*evals[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaMLFitEvals(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            count++;

            if (iterate == 0 || cdsA->algo->abort == 1)
                break;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        if (cdsA->algo->verbose != 0)
        {
            printf("\n>>>>> Final: %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f minvar:% -3.2e",
                   count, b, c, b / (c+1.0), logL, chi2, 2.0*b / (nd + 2.0*(1.0 + c)));
            fflush(NULL);
        }

        InvGammaAdjustVar(evals+missing, vlen-missing, cnum, evals+missing, b, c);

        xn1 = evals[missing];

        if (cdsA->algo->verbose != 0)
        {
            printf("\n1>>>> %8.3e %8.3e", evals[0], xn1);
            fflush(NULL);
        }

        expinvx = 1.0 / ExpInvXn(b, c, xn1);;

        for (i = 0; i < missing; ++i)
            evals[i] = expinvx;

        if (cdsA->algo->verbose != 0)
        {
            printf("\n2>>>> %8.3e %8.3e\n", xn1, evals[0]);
            fflush(NULL);
        }

        //chi2 = chi_sqr_adapt(evals, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
        //chi2 = chi_sqr_adapt(newvar, newlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);

        EigenReconSym(cdsA->CovMat, (const double **) evecs, evals, vlen);

        for (i = 0; i < vlen; ++i)
            evals[i] = cdsA->CovMat[i][i];
    }

//    for (i = 0; i < vlen; ++i)
//       printf("\n%3d %e", i, newvar[i]);

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaFitVars_fixed_c(CdsArray *cdsA, const double c, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b = 0.0, chi2 = 0.0, logL;
    int             i, count, newlen;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->covweight != 0)
    {
        double    **evecs = MatAlloc(vlen, vlen);

        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        eigensym((const double **) cdsA->CovMat, variance, evecs, vlen);

        RevVecIp(variance, vlen);

        for (i = newlen; i < vlen; ++i)
            variance[i] = 0.0;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;

            if (count > 3)
                b = c * HarmonicAve(newvar, vlen);
            else
                /* b = (vlen * c - c - 1.0) * HarmonicAve(newvar, newlen) / newlen; */
                b = vlen * c * HarmonicAve(newvar, newlen) / newlen;

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaFitVars_fixed_c(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        chi2 = chi_sqr_adapt(newvar, newlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);

        InvGammaAdjustCov(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];

        MatDestroy(&evecs);
    }
    else
    {
        memcpy(newvar, variance, vlen * sizeof(double));
        newlen = vlen - 3;

        count = 0;
        do
        {
            oldb = b;

/*             if (count > 3) */
/*             { */
/*                 b = c * HarmonicAve(newvar, vlen); */
/*             } */
/*             else */
            {
                qsort(newvar, vlen, sizeof(double), dblcmp);
                b = vlen * c * HarmonicAve(newvar, newlen) / newlen;
            }

            if (b < DBL_EPSILON)
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i]) / (nd + 2.0*(1.0 + c));
                break;
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
            }

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaFitVars_fixed_c(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        for (i = 0; i < vlen; ++i)
            variance[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        chi2 = chi_sqr_adapt(variance, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
    }

/*  for (i = 0; i < vlen; ++i) */
/*      printf("\n%3d %e", i, newvar[i]); */

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


void
InvGammaBayesFitVars_fixed_c(CdsArray *cdsA, const double c, int iterate)
{
    double         *newvar = NULL;
    double         *variance = cdsA->var;
    double          precision = cdsA->algo->precision;
    const int       vlen = cdsA->vlen, cnum = cdsA->cnum;
    double          nd, oldb, oldc, b = 0.0, chi2 = 0.0, logL;
    int             i, count, newlen;

    newvar = malloc(vlen * sizeof(double));
    nd = 3.0 * cnum;
    oldb = oldc = DBL_MAX;

    if (cdsA->algo->covweight != 0)
    {
        double    **evecs = MatAlloc(vlen, vlen);

        if (vlen - 3 < nd - 6)
            newlen = vlen - 3;
        else
            newlen = nd - 6;

        eigensym((const double **) cdsA->CovMat, variance, evecs, vlen);

        RevVecIp(variance, vlen);

        for (i = newlen; i < vlen; ++i)
            variance[i] = 0.0;

        memcpy(newvar, variance, vlen * sizeof(double));

        count = 0;
        do
        {
            oldb = b;

            if (count > 3)
                b = (c + 1.0) * HarmonicAve(newvar, vlen);
            else
                /* b = (vlen * c - c - 1.0) * HarmonicAve(newvar, newlen) / newlen; */
                b = vlen * (c + 1.0) * HarmonicAve(newvar, newlen) / newlen;

            for (i = 0; i < vlen; ++i)
                newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaFitVars_fixed_c(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        chi2 = chi_sqr_adapt(newvar, newlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);

        InvGammaAdjustCov(cdsA, b, c);

        for (i = 0; i < vlen; ++i)
            variance[i] = cdsA->CovMat[i][i];

        MatDestroy(&evecs);
    }
    else
    {
        memcpy(newvar, variance, vlen * sizeof(double));
        newlen = vlen - 3;

        count = 0;
        do
        {
            oldb = b;

/*             if (count > 3) */
/*             { */
/*                 b = c * HarmonicAve(newvar, vlen); */
/*             } */
/*             else */
            {
                qsort(newvar, vlen, sizeof(double), dblcmp);
                b = vlen * (c + 1.0) * HarmonicAve(newvar, newlen) / newlen;
            }

            if (b < DBL_EPSILON)
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i]) / (nd + 2.0*(1.0 + c));
                break;
            }
            else
            {
                for (i = 0; i < vlen; ++i)
                    newvar[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
            }

            if (cdsA->algo->verbose != 0)
            {
                printf(">>>>> %3d b:% -3.2e c:% -3.2e mode:%-10.5f logL:% -12.6f chi2:%-10.5f\n",
                       count, b, c, b / (c+1.0), logL, chi2);
                fflush(NULL);
            }

            if (count > 300)
            {
                printf("\n WARNING04: Failed to converge in InvGammaFitVars_fixed_c(), round %d\n    ",
                       cdsA->algo->rounds);
                fflush(NULL);
                break;
            }

            if (iterate == 0)
                break;

            ++count;
        }
        while(fabs(oldb - b) > fabs(b*precision));

        for (i = 0; i < vlen; ++i)
            variance[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));

        chi2 = chi_sqr_adapt(variance, vlen, 0, &logL, b, c, invgamma_pdf, invgamma_lnpdf, invgamma_int);
    }

/*  for (i = 0; i < vlen; ++i) */
/*      printf("\n%3d %e", i, newvar[i]); */

    cdsA->stats->hierarch_p1 = b;
    cdsA->stats->hierarch_p2 = c;
    cdsA->stats->hierarch_chi2 = chi2;

    free(newvar);
}


/* void */
/* InvGammaFitVars_c1(CdsArray *cdsA, double *b, double *c) */
/* { */
/*     const double   *data = (const double *) cdsA->var; */
/*     const int       num = cdsA->vlen; */
/*     double          ave; */
/*     int             i; */
/*  */
/*     ave = 0.0; */
/*     for (i = 0; i < num; ++i) */
/*         ave += (1.0 / data[i]); */
/*  */
/*     *b = num / ave; */
/*  */
/*     printf("\n Inverse Gamma: %7.3e %7.3e", *b, 1.0); */
/*     fflush(NULL); */
/* } */


void
InvGammaFitVars_GaussVarVar(CdsArray *cdsA, double *b, double *c)
{
    const double   *data = (const double *) cdsA->var;
    const int       num = cdsA->vlen;
    double          harmave;
    int             i;

    *c = 0.5 * (num + 4.0);

    harmave = 0.0;
    for (i = 0; i < num; ++i)
        harmave += (1.0 / data[i]);

    *b = *c * num / harmave;

    printf(" Inverse Gamma: %7.3e %7.3e\n", *b, *c);
    fflush(NULL);
}


void
InvGammaFitVars_Mode(CdsArray *cdsA, double *b, double *c, const double mode)
{
    double          logL;

    invgamma_mode_fit((const double *) cdsA->var, cdsA->vlen, b, c, mode, &logL);

    printf(" Inverse Gamma: %7.3e %7.3e\n", *b, *c);
    fflush(NULL);
}


void
InvGammaStacyFitVars(CdsArray *cdsA, double *b, double *c)
{
    const double   *data = (const double *) cdsA->var;
    const int       num = cdsA->vlen;
    double         *x = malloc(num * sizeof(double));
    double          logL;
    int             i;

    /* for robustness, toss the smallest variance */
    memcpy(x, data, num * sizeof(double));
    qsort(x, num, sizeof(double), dblcmp);

    for (i = 0; i < num-1; ++i)
        x[i] = (1.0 / x[i+1]);

    gamma_Stacyfit(x, num-1, b, c, &logL);
    *b = 1.0 / *b;

    /* printf("\n Inverse Gamma: %10.5f %10.5f", *b, *c); */

    free(x);
}


/* Fit a gamma distribution by method of moments (MMEs). */
void
InvGammaMMFitVars(CdsArray *cdsA, double *b, double *c)
{
    const double   *data = (const double *) cdsA->var;
    const int       num = cdsA->vlen;
    double         *x = malloc(num * sizeof(double));
    double          logL;
    int             i;

    for (i = 0; i < num; ++i)
        x[i] = (1.0 / data[i]);

    gamma_MMfit(x, num, b, c, &logL);
    *b = 1.0 / *b;

    /* printf("\n Inverse Gamma: %10.5f %10.5f", *b, *c); */

    free(x);
}


/* Var_IG = (ND/(ND + 2(c+1))) [(2b/ND) + Var_ML] */
void
InvGammaAdjustVars(CdsArray *cdsA, const double b, const double c)
{
    int             i;
    double         *variance = cdsA->var;
    const int       num = cdsA->vlen;
    const double    nd = 3.0 * cdsA->cnum;

    for (i = 0; i < num; ++i)
    {
        /* printf("\n %10.5f", variance[i]); */
        /* variance[i] = (nd/(nd + 2.0*(1.0 + c))) * (variance[i] + 2.0*b/nd); */
        variance[i] = (nd*variance[i] + 2.0*b) / (nd + 2.0*(1.0 + c));
        /* printf(" %10.5f", variance[i]); */
    }
}


void
InvGammaAdjustCov(CdsArray *cdsA, const double b, const double c)
{
    int             i, j;
    const int       vlen = cdsA->vlen;
    const double    nd = 3.0 * cdsA->cnum;

    for (i = 0; i < vlen; ++i)
        cdsA->CovMat[i][i] += 2.0 * b / nd;

    for (i = 0; i < vlen; ++i)
        for (j = 0; j < vlen; ++j)
            cdsA->CovMat[i][j] *= nd / (nd + 2.0 * (1.0 + c));
}


void
InvGammaAdjustCovMode(CdsArray *cdsA, const double b, const double c)
{
/*     int             i, j; */
/*     const int       vlen = cdsA->vlen; */
/*     const double    nd = 3.0 * cdsA->cnum; */
/*  */
/*          mode = b / (c+1.0); */
/*  */
/*          for (i = 0; i < vlen - newlen; ++i) */
/*              newvar[i] = mode; */
/*             for (i = 0; i < newlen; ++i) */
/*                 printf("\n%3d %e", i, newvar[i]); */
/*  */
/*     for (i = 0; i < vlen; ++i) */
/*         cdsA->CovMat[i][i] += 2.0 * b / nd; */
/*  */
/*     for (i = 0; i < vlen; ++i) */
/*         for (j = 0; j < vlen; ++j) */
/*             cdsA->CovMat[i][j] *= nd / (nd + 2.0 * (1.0 + c)); */
}


void
InvGammaAdjustCovND(CdsArray *cdsA, const double b, const double c)
{
    int             i, j;
    const int       vlen = cdsA->vlen;

    for (i = 0; i < vlen; ++i)
        cdsA->CovMat[i][i] += 2.0 * b;

    for (i = 0; i < vlen; ++i)
        for (j = 0; j < vlen; ++j)
            cdsA->CovMat[i][j] *= 1.0 / (3.0 + 2.0 * c);
}


/* Maximum likelihood fit to the reciprocal inverse gaussian distribution.
   My own derivation:
       mu = \Sum(1/x_i) / N
       lambda = N / ( \Sum{x_i + 1/(x_i mu^2)} - (2 N / mu))
*/
void
RecipInvGaussFitVars(CdsArray *cdsA, double *mu, double *lambda)
{
    const double   *data = (const double *) cdsA->var;
    const int       num = cdsA->vlen;
    double          sum_mu, sum_lambda;
    const double    numd = num;
    int             i;

    sum_mu = 0.0;
    for (i = 0; i < num; ++i)
        sum_mu += 1.0 / data[i];

    *mu = sum_mu / numd;

    sum_lambda = 0.0;
    for (i = 0; i < num; ++i)
        sum_lambda += (data[i] + 1.0 / (*mu * *mu * data[i]));

    *lambda = numd / (sum_lambda - 2.0 * numd / *mu);

    cdsA->stats->hierarch_p1 = *mu;
    cdsA->stats->hierarch_p2 = *lambda;
}


/* var_RIG = 1/(2 lambda)[(ND-1) - sqrt{(ND-1)^2 - 4 lambda(lambda/mu^2 +ND var_ML)}] */
void
RecipInvGaussAdjustVars(CdsArray *cdsA,
                            const double mu, const double lambda)
{
    int             i;
    double         *variance = cdsA->var;
    const int       num = cdsA->vlen;
    const double    nd1 = 3.0 * num - 1.0;

    for (i = 0; i < num; ++i)
    {
        printf(" %10.5f\n", variance[i]);
        variance[i] =
        nd1 - sqrt(nd1 * nd1 - 4.0 * lambda * (lambda/(mu*mu) + 3.0 * num * variance[i]));
        variance[i] *= (0.5 / lambda);
        printf(" %10.5f", variance[i]);
    }
}
