/* This file is part of the KDE project
   Copyright (C) 1998-2002 The KSpread Team <calligra-devel@kde.org>
   Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
   Copyright 2007 Sascha Pfau <MrPeacock@gmail.com>
   Copyright (C) 2010 Carlos Licea <carlos@kdab.com>
   Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
     Contact: Suresh Chande suresh.chande@nokia.com

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; only
   version 2 of the License.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

// built-in math functions
#include "MathModule.h"

// needed for RANDBINOM and so
#include <math.h>
#include <qmath.h>

#include "SheetsDebug.h"
#include "FunctionModuleRegistry.h"
#include "Function.h"
#include "FunctionRepository.h"
#include "ValueCalc.h"
#include "ValueConverter.h"

// needed for SUBTOTAL:
#include "Cell.h"
#include "Sheet.h"
#include "RowColumnFormat.h"
#include "RowFormatStorage.h"

// needed by MDETERM and MINVERSE
// Don't show this warning: it's an issue in eigen
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif

#ifdef ENABLE_EIGEN
#include <Eigen/LU>
#endif

using namespace Calligra::Sheets;

// RANDBINOM and RANDNEGBINOM won't support arbitrary precision

// prototypes
Value func_abs(valVector args, ValueCalc *calc, FuncExtra *);
Value func_ceil(valVector args, ValueCalc *calc, FuncExtra *);
Value func_ceiling(valVector args, ValueCalc *calc, FuncExtra *);
Value func_count(valVector args, ValueCalc *calc, FuncExtra *);
Value func_counta(valVector args, ValueCalc *calc, FuncExtra *);
Value func_countblank(valVector args, ValueCalc *calc, FuncExtra *);
Value func_countif(valVector args, ValueCalc *calc, FuncExtra *);
Value func_countifs(valVector args, ValueCalc *calc, FuncExtra *e);
Value func_cur(valVector args, ValueCalc *calc, FuncExtra *);
Value func_div(valVector args, ValueCalc *calc, FuncExtra *);
Value func_eps(valVector args, ValueCalc *calc, FuncExtra *);
Value func_even(valVector args, ValueCalc *calc, FuncExtra *);
Value func_exp(valVector args, ValueCalc *calc, FuncExtra *);
Value func_fact(valVector args, ValueCalc *calc, FuncExtra *);
Value func_factdouble(valVector args, ValueCalc *calc, FuncExtra *);
Value func_fib(valVector args, ValueCalc *calc, FuncExtra *);
Value func_floor(valVector args, ValueCalc *calc, FuncExtra *);
Value func_gamma(valVector args, ValueCalc *calc, FuncExtra *);
Value func_gcd(valVector args, ValueCalc *calc, FuncExtra *);
Value func_int(valVector args, ValueCalc *calc, FuncExtra *);
Value func_inv(valVector args, ValueCalc *calc, FuncExtra *);
Value func_kproduct(valVector args, ValueCalc *calc, FuncExtra *);
Value func_lcm(valVector args, ValueCalc *calc, FuncExtra *);
Value func_ln(valVector args, ValueCalc *calc, FuncExtra *);
Value func_log2(valVector args, ValueCalc *calc, FuncExtra *);
Value func_log10(valVector args, ValueCalc *calc, FuncExtra *);
Value func_logn(valVector args, ValueCalc *calc, FuncExtra *);
Value func_max(valVector args, ValueCalc *calc, FuncExtra *);
Value func_maxa(valVector args, ValueCalc *calc, FuncExtra *);
Value func_mdeterm(valVector args, ValueCalc *calc, FuncExtra *);
Value func_min(valVector args, ValueCalc *calc, FuncExtra *);
Value func_mina(valVector args, ValueCalc *calc, FuncExtra *);
Value func_minverse(valVector args, ValueCalc* calc, FuncExtra*);
Value func_mmult(valVector args, ValueCalc *calc, FuncExtra *);
Value func_mod(valVector args, ValueCalc *calc, FuncExtra *);
Value func_mround(valVector args, ValueCalc *calc, FuncExtra *);
Value func_mult(valVector args, ValueCalc *calc, FuncExtra *);
Value func_multinomial(valVector args, ValueCalc *calc, FuncExtra *);
Value func_munit(valVector args, ValueCalc* calc, FuncExtra*);
Value func_odd(valVector args, ValueCalc *calc, FuncExtra *);
Value func_pow(valVector args, ValueCalc *calc, FuncExtra *);
Value func_quotient(valVector args, ValueCalc *calc, FuncExtra *);
Value func_product(valVector args, ValueCalc *calc, FuncExtra *);
Value func_rand(valVector args, ValueCalc *calc, FuncExtra *);
Value func_randbetween(valVector args, ValueCalc *calc, FuncExtra *);
Value func_randbernoulli(valVector args, ValueCalc *calc, FuncExtra *);
Value func_randbinom(valVector args, ValueCalc *calc, FuncExtra *);
Value func_randexp(valVector args, ValueCalc *calc, FuncExtra *);
Value func_randnegbinom(valVector args, ValueCalc *calc, FuncExtra *);
Value func_randnorm(valVector args, ValueCalc *calc, FuncExtra *);
Value func_randpoisson(valVector args, ValueCalc *calc, FuncExtra *);
Value func_rootn(valVector args, ValueCalc *calc, FuncExtra *);
Value func_round(valVector args, ValueCalc *calc, FuncExtra *);
Value func_rounddown(valVector args, ValueCalc *calc, FuncExtra *);
Value func_roundup(valVector args, ValueCalc *calc, FuncExtra *);
Value func_seriessum(valVector args, ValueCalc *calc, FuncExtra *);
Value func_sign(valVector args, ValueCalc *calc, FuncExtra *);
Value func_sqrt(valVector args, ValueCalc *calc, FuncExtra *);
Value func_sqrtpi(valVector args, ValueCalc *calc, FuncExtra *);
Value func_subtotal(valVector args, ValueCalc *calc, FuncExtra *);
Value func_sum(valVector args, ValueCalc *calc, FuncExtra *);
Value func_suma(valVector args, ValueCalc *calc, FuncExtra *);
Value func_sumif(valVector args, ValueCalc *calc, FuncExtra *);
Value func_sumifs(valVector args, ValueCalc *calc, FuncExtra *);     //here
Value func_sumsq(valVector args, ValueCalc *calc, FuncExtra *);
Value func_transpose(valVector args, ValueCalc *calc, FuncExtra *);
Value func_trunc(valVector args, ValueCalc *calc, FuncExtra *);

// Value func_multipleOP (valVector args, ValueCalc *calc, FuncExtra *);

CALLIGRA_SHEETS_EXPORT_FUNCTION_MODULE("kspreadmathmodule.json", MathModule)

MathModule::MathModule(QObject* parent, const QVariantList&)
        : FunctionModule(parent)
{
    Function *f;

    /*
      f = new Function ("MULTIPLEOPERATIONS", func_multipleOP);
    add(f);
    */

    // functions that don't take array parameters
    f = new Function("ABS",           func_abs);
    add(f);
    f = new Function("CEIL",          func_ceil);
    add(f);
    f = new Function("CEILING",       func_ceiling);
    f->setParamCount(1, 3);
    add(f);
    f = new Function("CUR",           func_cur);
    add(f);
    f = new Function("EPS",           func_eps);
    f->setParamCount(0);
    add(f);
    f = new Function("EVEN",          func_even);
    add(f);
    f = new Function("EXP",           func_exp);
    add(f);
    f = new Function("FACT",          func_fact);
    add(f);
    f = new Function("FACTDOUBLE",    func_factdouble);
    f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETFACTDOUBLE");
    add(f);
    f = new Function("FIB",           func_fib);  // Calligra Sheets-specific, like Quattro-Pro's FIB
    add(f);
    f = new Function("FLOOR",         func_floor);
    f->setParamCount(1, 3);
    add(f);
    f = new Function("GAMMA",         func_gamma);
    add(f);
    f = new Function("INT",           func_int);
    add(f);
    f = new Function("INV",           func_inv);
    add(f);
    f = new Function("LN",            func_ln);
    add(f);
    f = new Function("LOG",           func_logn);
    f->setParamCount(1, 2);
    add(f);
    f = new Function("LOG2",          func_log2);
    add(f);
    f = new Function("LOG10",         func_log10);
    add(f);
    f = new Function("LOGN",          func_logn);
    f->setParamCount(2);
    add(f);
    f = new Function("MOD",           func_mod);
    f->setParamCount(2);
    add(f);
    f = new Function("MROUND",        func_mround);
    f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMROUND");
    f->setParamCount(2);
    add(f);
    f = new Function("MULTINOMIAL",   func_multinomial);
    f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMULTINOMIAL");
    f->setParamCount(1, -1);
    add(f);
    f = new Function("ODD",           func_odd);
    add(f);
    f = new Function("POW",         func_pow);
    f->setParamCount(2);
    add(f);
    f = new Function("POWER",         func_pow);
    f->setParamCount(2);
    add(f);
    f = new Function("QUOTIENT",      func_quotient);
    f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETQUOTIENT");
    f->setParamCount(2);
    add(f);
    f = new Function("RAND",          func_rand);
    f->setParamCount(0);
    add(f);
    f = new Function("RANDBERNOULLI", func_randbernoulli);
    add(f);
    f = new Function("RANDBETWEEN",   func_randbetween);
    f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETRANDBETWEEN");
    f->setParamCount(2);
    add(f);
    f = new Function("RANDBINOM",     func_randbinom);
    f->setParamCount(2);
    add(f);
    f = new Function("RANDEXP",       func_randexp);
    add(f);
    f = new Function("RANDNEGBINOM",  func_randnegbinom);
    f->setParamCount(2);
    add(f);
    f = new Function("RANDNORM",      func_randnorm);
    f->setParamCount(2);
    add(f);
    f = new Function("RANDPOISSON",   func_randpoisson);
    add(f);
    f = new Function("ROOTN",         func_rootn);
    f->setParamCount(2);
    add(f);
    f = new Function("ROUND",         func_round);
    f->setParamCount(1, 2);
    add(f);
    f = new Function("ROUNDDOWN",     func_rounddown);
    f->setParamCount(1, 2);
    add(f);
    f = new Function("ROUNDUP",       func_roundup);
    f->setParamCount(1, 2);
    add(f);
    f = new Function("SIGN",          func_sign);
    add(f);
    f = new Function("SQRT",          func_sqrt);
    add(f);
    f = new Function("SQRTPI",        func_sqrtpi);
    f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETSQRTPI");
    add(f);
    f = new Function("TRUNC",         func_trunc);
    f->setParamCount(1, 2);
    add(f);

    // functions that operate over arrays
    f = new Function("COUNT",         func_count);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("COUNTA",        func_counta);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("COUNTBLANK",    func_countblank);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("COUNTIF",       func_countif);
    f->setParamCount(2);
    f->setAcceptArray();
    f->setNeedsExtra(true);
    add(f);
    f = new Function("COUNTIFS",         func_countifs);
    f->setParamCount(2, -1);
    f->setAcceptArray();
    f->setNeedsExtra(true);
    add(f);
    f = new Function("DIV",           func_div);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("G_PRODUCT",     func_kproduct);  // Gnumeric compatibility
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("GCD",           func_gcd);
    f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETGCD");
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("KPRODUCT",      func_kproduct);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("LCM",           func_lcm);
    f->setAlternateName("COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETLCM");
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("MAX",           func_max);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("MAXA",          func_maxa);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("MDETERM",          func_mdeterm);
    f->setParamCount(1);
    f->setAcceptArray();
    add(f);
    f = new Function("MIN",           func_min);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("MINA",          func_mina);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("MINVERSE",         func_minverse);
    f->setParamCount(1);
    f->setAcceptArray();
    add(f);
    f = new Function("MMULT",          func_mmult);
    f->setParamCount(2);
    f->setAcceptArray();
    add(f);
    f = new Function("MULTIPLY",      func_product);   // same as PRODUCT
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("MUNIT",         func_munit);
    f->setParamCount(1);
    add(f);
    f = new Function("PRODUCT",       func_product);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("SERIESSUM",     func_seriessum);
    f->setParamCount(3, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("SUM",           func_sum);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("SUMA",          func_suma);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("SUBTOTAL",      func_subtotal);
    f->setParamCount(2);
    f->setAcceptArray();
    f->setNeedsExtra(true);
    add(f);
    f = new Function("SUMIF",         func_sumif);
    f->setParamCount(2, 3);
    f->setAcceptArray();
    f->setNeedsExtra(true);
    add(f);
    f = new Function("SUMIFS",         func_sumifs);
    f->setParamCount(3, -1);
    f->setAcceptArray();
    f->setNeedsExtra(true);
    add(f);
    f = new Function("SUMSQ",         func_sumsq);
    f->setParamCount(1, -1);
    f->setAcceptArray();
    add(f);
    f = new Function("TRANSPOSE",     func_transpose);
    f->setParamCount(1);
    f->setAcceptArray();
    add(f);
}

QString MathModule::descriptionFileName() const
{
    return QString("math.xml");
}

// Function: SQRT
Value func_sqrt(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value arg = args[0];
    if (calc->gequal(arg, Value(0.0)))
        return calc->sqrt(arg);
    else
        return Value::errorVALUE();
}

// Function: SQRTPI
Value func_sqrtpi(valVector args, ValueCalc *calc, FuncExtra *)
{
    // sqrt (val * PI)
    Value arg = args[0];
    if (calc->gequal(arg, Value(0.0)))
        return calc->sqrt(calc->mul(args[0], calc->pi()));
    else
        return Value::errorVALUE();
}

// Function: ROOTN
Value func_rootn(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->pow(args[0], calc->div(Value(1), args[1]));
}

// Function: CUR
Value func_cur(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->pow(args[0], Value(1.0 / 3.0));
}

// Function: ABS
Value func_abs(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->abs(args[0]);
}

// Function: exp
Value func_exp(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->exp(args[0]);
}

// Function: ceil
Value func_ceil(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->roundUp(args[0], Value(0));
}

// Function: ceiling
Value func_ceiling(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value number = args[0];
    Value res;
    if (args.count() >= 2)
        res = args[1];
    else
        res = calc->gequal(number, Value(0.0)) ? Value(1.0) : Value(-1.0);
    bool mode = (args.count() >= 3) ? calc->isZero (args[2]) : true;

    // short-circuit, and allow CEILING(0;0) to give 0 (which is correct)
    // instead of DIV0 error
    if (calc->isZero(number))
        return Value(0.0);

    if (calc->isZero(res))
        return Value::errorDIV0();

    Value d = calc->div(number, res);
    if (calc->greater(Value(0), d))
        return Value::errorNUM();

    Value rud = calc->roundDown(d);
    if (calc->approxEqual(rud, d))
        d = calc->mul(rud, res);
    else
    {
        // positive number or mode is 0 - round up
        if ((!mode) || calc->gequal (number, Value(0))) rud = calc->roundUp(d);
        d = calc->mul (rud, res);
    }

    return d;
}

// Function: FLOOR
Value func_floor(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (calc->approxEqual(args[0], Value(0.0)))
        return Value(0);
    Number number = args[0].asFloat();

    Number significance;
    if (args.count() >= 2) { // we have the optional "significance" argument
        significance = args[1].asFloat();
        // Sign of number and significance must match.
        if (calc->gequal(args[0], Value(0.0)) != calc->gequal(args[1], Value(0.0)))
            return Value::errorVALUE();
    } else // use 1 or -1, depending on the sign of the first argument
        significance = calc->gequal(args[0], Value(0.0)) ? 1.0 : -1.0;
    if (calc->approxEqual(Value(significance), Value(0.0)))
        return Value(0);

    const bool mode = (args.count() == 3) ? (args[2].asFloat() != 0.0) : false;

    Number result;
    if (mode) // round towards zero
        result = ((int)(number / significance)) * significance;
    else { // round towards negative infinity
        result = number / significance; // always positive, because signs match
        if (calc->gequal(args[0], Value(0.0))) // positive values
            result = floor(result) * significance;
        else // negative values
            result = ceil(result) * significance;
    }
    return Value(result);
}

// Function: GAMMA
Value func_gamma(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->GetGamma(args[0]);
}

// Function: ln
Value func_ln(valVector args, ValueCalc *calc, FuncExtra *)
{
    if ((args [0].isNumber() == false) || args[0].asFloat() <= 0)
        return Value::errorNUM();
    return calc->ln(args[0]);
}

// Function: LOGn
Value func_logn(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (args [0].isError())
        return args [0];
    if (args [0].isEmpty())
        return Value::errorNUM();
    if (args [0].isNumber() == false)
        return Value::errorVALUE();
    if (args[0].asFloat() <= 0)
        return Value::errorNUM();
    if (args.count() == 2) {
        if (args [1].isError())
            return args [1];
        if (args [1].isEmpty())
            return Value::errorNUM();
        if (args [1].isNumber() == false)
            return Value::errorVALUE();
        if (args [1].asFloat() <= 0)
            return Value::errorNUM();
        return calc->log(args[0], args[1]);
    } else
        return calc->log(args[0]);
}

// Function: LOG2
Value func_log2(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->log(args[0], Value(2.0));
}

// Function: LOG10
Value func_log10(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (args [0].isError())
        return args [0];
    if ((args [0].isNumber() == false) || (args[0].asFloat() <= 0))
        return Value::errorNUM();
    return calc->log(args[0]);
}

// Function: sum
Value func_sum(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->sum(args, false);
}

// Function: suma
Value func_suma(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->sum(args, true);
}

// Function: SUMIF
Value func_sumif(valVector args, ValueCalc *calc, FuncExtra *e)
{
    Value checkRange = args[0];
    QString condition = calc->conv()->asString(args[1]).asString();
    Condition cond;
    calc->getCond(cond, Value(condition));

    if (args.count() == 3) {
        Cell sumRangeStart(e->regions[2].firstSheet(), e->regions[2].firstRange().topLeft());
        return calc->sumIf(sumRangeStart, checkRange, cond);
    } else {
        return calc->sumIf(checkRange, cond);
    }
}

//Function: SUMIFS
Value func_sumifs(valVector args, ValueCalc *calc, FuncExtra *e)
{
    int lim = (int) (args.count()-1)/2;

    QList<Value> c_Range;
    QStringList condition;
    QList<Condition> cond;

    c_Range.append(args.value(0));           //first element - range to be operated on

    for (int i = 1; i < args.count(); i += 2) {
        c_Range.append(args[i]);
        condition.append(calc->conv()->asString(args[i+1]).asString());
        Condition c;
        calc->getCond(c, Value(condition.last()));
        cond.append(c);
    }
    Cell sumRangeStart(e->sheet, e->ranges[2].col1, e->ranges[2].row1);
    return calc->sumIfs(sumRangeStart, c_Range, cond, lim);
}

// Function: product
Value func_product(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->product(args, Value(0.0));
}

// Function: seriessum
Value func_seriessum(valVector args, ValueCalc *calc, FuncExtra *)
{
    double fX = calc->conv()->asFloat(args[0]).asFloat();
    double fN = calc->conv()->asFloat(args[1]).asFloat();
    double fM = calc->conv()->asFloat(args[2]).asFloat();

    if (fX == 0.0 && fN == 0.0)
        return Value::errorNUM();

    double res = 0.0;

    if (fX != 0.0) {

        for (unsigned int i = 0 ; i < args[3].count(); i++) {
            res += args[3].element(i).asFloat() * pow(fX, fN);
            fN += fM;
        }
    }

    return Value(res);
}

// Function: kproduct
Value func_kproduct(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->product(args, Value(1.0));
}

// Function: DIV
Value func_div(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value val = args[0];
    for (int i = 1; i < args.count(); ++i) {
        val = calc->div(val, args[i]);
        if (val.isError())
            return val;
    }
    return val;
}

// Function: SUMSQ
Value func_sumsq(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value res;
    calc->arrayWalk(args, res, calc->awFunc("sumsq"), Value(0));
    return res;
}

// Function: MAX
Value func_max(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value m = calc->max(args, false);
    return m.isEmpty() ? Value(0.0) : m;
}

// Function: MAXA
Value func_maxa(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value m = calc->max(args);
    return m.isEmpty() ? Value(0.0) : m;
}

// Function: MIN
Value func_min(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value m = calc->min(args, false);
    return m.isEmpty() ? Value(0.0) : m;
}

// Function: MINA
Value func_mina(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value m = calc->min(args, true);
    return m.isEmpty() ? Value(0.0) : m;
}

// Function: INT
Value func_int(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->conv()->asInteger(args[0]);
}

// Function: QUOTIENT
Value func_quotient(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (calc->isZero(args[1]))
        return Value::errorDIV0();

    double res = calc->conv()->toFloat(calc->div(args[0], args[1]));
    if (res < 0)
        res = ceil(res);
    else
        res = floor(res);

    return Value(res);
}

// Function: eps
Value func_eps(valVector, ValueCalc *calc, FuncExtra *)
{
    return calc->eps();
}

Value func_randexp(valVector args, ValueCalc *calc, FuncExtra *)
{
    // -1 * d * log (random)
    return calc->mul(calc->mul(args[0], Value(-1)), calc->random());
}

Value func_randbinom(valVector args, ValueCalc *calc, FuncExtra *)
{
    // this function will not support arbitrary precision

    double d  = numToDouble(calc->conv()->toFloat(args[0]));
    int    tr = calc->conv()->toInteger(args[1]);

    if (d < 0 || d > 1)
        return Value::errorVALUE();

    if (tr < 0)
        return Value::errorVALUE();

    // taken from gnumeric
    double x = pow(1 - d, tr);
    double r = (double) rand() / (RAND_MAX + 1.0);
    double t = x;
    int i = 0;

    while (r > t) {
        x *= (((tr - i) * d) / ((1 + i) * (1 - d)));
        i++;
        t += x;
    }

    return Value(i);
}

Value func_randnegbinom(valVector args, ValueCalc *calc, FuncExtra *)
{
    // this function will not support arbitrary precision

    double d  = numToDouble(calc->conv()->toFloat(args[0]));
    int    f = calc->conv()->toInteger(args[1]);

    if (d < 0 || d > 1)
        return Value::errorVALUE();

    if (f < 0)
        return Value::errorVALUE();

    // taken from Gnumeric
    double x = pow(d, f);
    double r = (double) rand() / (RAND_MAX + 1.0);
    double t = x;
    int i = 0;

    while (r > t) {
        x *= (((f + i) * (1 - d)) / (1 + i)) ;
        i++;
        t += x;
    }

    return Value(i);
}

Value func_randbernoulli(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value rnd = calc->random();
    return Value(calc->greater(rnd, args[0]) ? 1.0 : 0.0);
}

Value func_randnorm(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value mu = args[0];
    Value sigma = args[1];

    //using polar form of the Box-Muller transformation
    //refer to http://www.taygeta.com/random/gaussian.html for more info

    Value x1, x2, w;
    do {
        // x1,x2 = 2 * random() - 1
        x1 = calc->random(2.0);
        x2 = calc->random(2.0);
        x1 = calc->sub(x1, 1);
        x1 = calc->sub(x2, 1);
        w = calc->add(calc->sqr(x1), calc->sqr(x2));
    } while (calc->gequal(w, Value(1.0)));    // w >= 1.0

    //sqrt ((-2.0 * log (w)) / w) :
    w = calc->sqrt(calc->div(calc->mul(Value(-2.0), calc->ln(w)), w));
    Value res = calc->mul(x1, w);

    res = calc->add(calc->mul(res, sigma), mu);    // res*sigma + mu
    return res;
}

Value func_randpoisson(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (calc->lower(args[0], Value(0)))
        return Value::errorVALUE();

    // taken from Gnumeric...
    Value x = calc->exp(calc->mul(Value(-1), args[0]));     // e^(-A)
    Value r = calc->random();
    Value t = x;
    int i = 0;

    while (calc->greater(r, t)) {    // r > t
        x = calc->mul(x, calc->div(args[0], i + 1));    // x *= (A/(i+1))
        t = calc->add(t, x);     //t += x
        i++;
    }

    return Value(i);
}

// Function: rand
Value func_rand(valVector, ValueCalc *calc, FuncExtra *)
{
    return calc->random();
}

// Function: RANDBETWEEN
Value func_randbetween(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value v1 = args[0];
    Value v2 = args[1];
    if (calc->greater(v2, v1)) {
        v1 = args[1];
        v2 = args[0];
    }
    return calc->add(v1, calc->random(calc->sub(v2, v1)));
}

// Function: POW
Value func_pow(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->pow(args[0], args[1]);
}

// Function: MOD
Value func_mod(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->mod(args[0], args[1]);
}

// Function: fact
Value func_fact(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (args[0].isInteger() || args[0].asInteger() > 0)
        return calc->fact(args[0]);
    else
        return Value::errorNUM();
}

// Function: FACTDOUBLE
Value func_factdouble(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (args[0].isInteger() || args[0].asInteger() > 0)
        return calc->factDouble(args[0]);
    else
        return Value::errorNUM();
}

// Function: MULTINOMIAL
Value func_multinomial(valVector args, ValueCalc *calc, FuncExtra *)
{
    // (a+b+c)! / a!b!c!  (any number of params possible)
    Value num = Value(0), den = Value(1);
    for (int i = 0; i < args.count(); ++i) {
        num = calc->add(num, args[i]);
        den = calc->mul(den, calc->fact(args[i]));
    }
    num = calc->fact(num);
    return calc->div(num, den);
}

// Function: sign
Value func_sign(valVector args, ValueCalc *calc, FuncExtra *)
{
    return Value(calc->sign(args[0]));
}

// Function: INV
Value func_inv(valVector args, ValueCalc *calc, FuncExtra *)
{
    return calc->mul(args[0], -1);
}

// Function: MROUND
Value func_mround(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value d = args[0];
    Value m = args[1];

    // signs must be the same
    if ((calc->greater(d, Value(0)) && calc->lower(m, Value(0)))
            || (calc->lower(d, Value(0)) && calc->greater(m, Value(0))))
        return Value::errorVALUE();

    int sign = 1;

    if (calc->lower(d, Value(0))) {
        sign = -1;
        d = calc->mul(d, Value(-1));
        m = calc->mul(m, Value(-1));
    }

    // from gnumeric:
    Value mod = calc->mod(d, m);
    Value div = calc->sub(d, mod);

    Value result = div;
    if (calc->gequal(mod, calc->div(m, Value(2))))  // mod >= m/2
        result = calc->add(result, m);      // result += m
    result = calc->mul(result, sign);     // add the sign

    return result;
}

// Function: ROUNDDOWN
Value func_rounddown(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (args.count() == 2) {
        if (calc->greater(args[0], 0.0))
            return calc->roundDown(args[0], args[1]);
        else
            return calc->roundUp(args[0], args[1]);
    }

    if (calc->greater(args[0], 0.0))
        return calc->roundDown(args[0], 0);
    else
        return calc->roundUp(args[0], 0);
}

// Function: ROUNDUP
Value func_roundup(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (args.count() == 2) {
        if (calc->greater(args[0], 0.0))
            return calc->roundUp(args[0], args[1]);
        else
            return calc->roundDown(args[0], args[1]);
    }

    if (calc->greater(args[0], 0.0))
        return calc->roundUp(args[0], 0);
    else
        return calc->roundDown(args[0], 0);
}

// Function: ROUND
Value func_round(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (args.count() == 2)
        return calc->round(args[0], args[1]);
    return calc->round(args[0], 0);
}

// Function: EVEN
Value func_even(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (calc->greater(args[0], 0.0)) {
        const Value value = calc->roundUp(args[0], 0);
        return calc->isZero(calc->mod(value, Value(2))) ? value : calc->add(value, Value(1));
    } else {
        const Value value = calc->roundDown(args[0], 0);
        return calc->isZero(calc->mod(value, Value(2))) ? value : calc->sub(value, Value(1));
    }
}

// Function: ODD
Value func_odd(valVector args, ValueCalc *calc, FuncExtra *)
{
    if (calc->gequal(args[0], Value(0))) {
        const Value value = calc->roundUp(args[0], 0);
        return calc->isZero(calc->mod(value, Value(2))) ? calc->add(value, Value(1)) : value;
    } else {
        const Value value = calc->roundDown(args[0], 0);
        return calc->isZero(calc->mod(value, Value(2))) ? calc->add(value, Value(-1)) : value;
    }
}

Value func_trunc(valVector args, ValueCalc *calc, FuncExtra *)
{
    Q_UNUSED(calc)
    Number result = args[0].asFloat();
    if (args.count() == 2)
        result = result * ::qPow(10, (int)args[1].asInteger());
    result = (args[0].asFloat() < 0) ? -(qint64)(-result) : (qint64)result;
    if (args.count() == 2)
        result = result * ::qPow(10, -(int)args[1].asInteger());
    return Value(result);
}

// Function: COUNT
Value func_count(valVector args, ValueCalc *calc, FuncExtra *)
{
    return Value(calc->count(args, false));
}

// Function: COUNTA
Value func_counta(valVector args, ValueCalc *calc, FuncExtra *)
{
    return Value(calc->count(args));
}

// Function: COUNTBLANK
Value func_countblank(valVector args, ValueCalc *, FuncExtra *)
{
    int cnt = 0;
    for (int i = 0; i < args.count(); ++i)
        if (args[i].isArray()) {
            int rows = args[i].rows();
            int cols = args[i].columns();
            for (int r = 0; r < rows; ++r)
                for (int c = 0; c < cols; ++c)
                    if (args[i].element(c, r).isEmpty())
                        cnt++;
        } else if (args[i].isEmpty())
            cnt++;
    return Value(cnt);
}

// Function: COUNTIF
Value func_countif(valVector args, ValueCalc *calc, FuncExtra *e)
{
    // the first parameter must be a reference
    if ((e->ranges[0].col1 == -1) || (e->ranges[0].row1 == -1))
        return Value::errorNA();

    Value range = args[0];
    QString condition = calc->conv()->asString(args[1]).asString();

    Condition cond;
    calc->getCond(cond, Value(condition));

    return Value(calc->countIf(range, cond));
}

// Function: COUNTIFS
Value func_countifs(valVector args, ValueCalc *calc, FuncExtra *e)
{
    // the first parameter must be a reference
    if ((e->ranges[0].col1 == -1) || (e->ranges[0].row1 == -1))
        return Value::errorNA();

    int lim = (int) (args.count()-1)/2;

    QList<Value> c_Range;
    QStringList condition;
    QList<Condition> cond;

    for (int i = 0; i < args.count(); i += 2) {
        c_Range.append(args[i]);
        condition.append(calc->conv()->asString(args[i+1]).asString());
        Condition c;
        calc->getCond(c, Value(condition.last()));
        cond.append(c);
    }
    Cell cntRangeStart(e->sheet, e->ranges[2].col1, e->ranges[2].row1);
    return calc->countIfs(cntRangeStart, c_Range, cond, lim);
}

// Function: FIB
Value func_fib(valVector args, ValueCalc *calc, FuncExtra *)
{
    /*
    Lucas' formula for the nth Fibonacci number F(n) is given by

             ((1+sqrt(5))/2)^n - ((1-sqrt(5))/2)^n
      F(n) = ------------------------------------- .
                             sqrt(5)

    */
    Value n = args[0];
    if (!n.isNumber())
        return Value::errorVALUE();

    if (!calc->greater(n, Value(0.0)))
        return Value::errorNUM();

    Value s = calc->sqrt(Value(5.0));
    // u1 = ((1+sqrt(5))/2)^n
    Value u1 = calc->pow(calc->div(calc->add(Value(1), s), Value(2)), n);
    // u2 = ((1-sqrt(5))/2)^n
    Value u2 = calc->pow(calc->div(calc->sub(Value(1), s), Value(2)), n);

    Value result = calc->div(calc->sub(u1, u2), s);
    return result;
}

static Value func_gcd_helper(const Value &val, ValueCalc *calc)
{
    Value res(0);
    if (!val.isArray())
        return val;
    for (uint row = 0; row < val.rows(); ++row)
        for (uint col = 0; col < val.columns(); ++col) {
            Value v = val.element(col, row);
            if (v.isArray())
                v = func_gcd_helper(v, calc);
            res = calc->gcd(res, calc->roundDown(v));
        }
    return res;
}

// Function: GCD
Value func_gcd(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value result = Value(0);
    for (int i = 0; i < args.count(); ++i) {
        if (args[i].isArray()) {
            result = calc->gcd(result, func_gcd_helper(args[i], calc));
        } else {
            if (args[i].isNumber() && args[i].asInteger() >= 0) {
                result = calc->gcd(result, calc->roundDown(args[i]));
            } else {
                return Value::errorNUM();
            }
        }
    }
    return result;
}

static Value func_lcm_helper(const Value &val, ValueCalc *calc)
{
    Value res = Value(0);
    if (!val.isArray())
        return val;
    for (unsigned int row = 0; row < val.rows(); ++row)
        for (unsigned int col = 0; col < val.columns(); ++col) {
            Value v = val.element(col, row);
            if (v.isArray())
                v = func_lcm_helper(v, calc);
            res = calc->lcm(res, calc->roundDown(v));
        }
    return res;
}

// Function: lcm
Value func_lcm(valVector args, ValueCalc *calc, FuncExtra *)
{
    Value result = Value(0);
    for (int i = 0; i < args.count(); ++i) {
        if (args[i].isArray()) {
            result = calc->lcm(result, func_lcm_helper(args[i], calc));
        } else {
            if (args[i].isNumber() == false) {
                return Value::errorNUM();
            } else {
                // its a number
                if (args[i].asInteger() < 0) {
                    return Value::errorNUM();
                } else if (args[i].asInteger() == 0) {
                    return Value(0);
                } else { // number > 0
                    result = calc->lcm(result, calc->roundDown(args[i]));
                }
            }
        }
    }
    return result;
}

#ifdef ENABLE_EIGEN
static Eigen::MatrixXd convert(const Value& matrix, ValueCalc *calc)
{
    const int rows = matrix.rows(), cols = matrix.columns();
    Eigen::MatrixXd eMatrix(rows, cols);
    for (int row = 0; row < rows; ++row) {
        for (int col = 0; col < cols; ++col) {
            eMatrix(row, col) = numToDouble(calc->conv()->toFloat(matrix.element(col, row)));
        }
    }
    return eMatrix;
}

static Value convert(const Eigen::MatrixXd& eMatrix)
{
    const int rows = eMatrix.rows(), cols = eMatrix.cols();
    Value matrix(Value::Array);
    for (int row = 0; row < rows; ++row) {
        for (int col = 0; col < cols; ++col) {
            matrix.setElement(col, row, Value(eMatrix(row, col)));
        }
    }
    return matrix;
}
#endif

// Function: MDETERM
Value func_mdeterm(valVector args, ValueCalc* calc, FuncExtra*)
{
    Value matrix = args[0];
    if (matrix.columns() != matrix.rows() || matrix.rows() < 1)
        return Value::errorVALUE();
#ifdef ENABLE_EIGEN
    const Eigen::MatrixXd eMatrix = convert(matrix, calc);

    return Value(eMatrix.determinant());
#else
    return Value::errorVALUE();
#endif
}

// Function: MINVERSE
Value func_minverse(valVector args, ValueCalc* calc, FuncExtra*)
{
    Value matrix = args[0];
    if (matrix.columns() != matrix.rows() || matrix.rows() < 1)
        return Value::errorVALUE();
#ifdef ENABLE_EIGEN
    Eigen::MatrixXd eMatrix = convert(matrix, calc);
    Eigen::FullPivLU<Eigen::MatrixXd> lu(eMatrix);
    if (lu.isInvertible()) {
        Eigen::MatrixXd eMatrixInverse = lu.inverse();
        return convert(eMatrixInverse);
    } else
#endif
        return Value::errorDIV0();
}

// Function: mmult
Value func_mmult(valVector args, ValueCalc *calc, FuncExtra *)
{
#ifdef ENABLE_EIGEN
    const Eigen::MatrixXd eMatrix1 = convert(args[0], calc);
    const Eigen::MatrixXd eMatrix2 = convert(args[1], calc);

    if (eMatrix1.cols() != eMatrix2.rows())    // row/column counts must match
        return Value::errorVALUE();

    return convert(eMatrix1 * eMatrix2);
#else
    return Value::errorVALUE();
#endif
}

// Function: MUNIT
Value func_munit(valVector args, ValueCalc* calc, FuncExtra*)
{
    const int dim = calc->conv()->asInteger(args[0]).asInteger();
    if (dim < 1)
        return Value::errorVALUE();
    Value result(Value::Array);
    for (int row = 0; row < dim; ++row)
        for (int col = 0; col < dim; ++col)
            result.setElement(col, row, Value(col == row ? 1 : 0));
    return result;
}

// Function: SUBTOTAL
// This function requires access to the Sheet and so on, because
// it needs to check whether cells contain the SUBTOTAL formula or not ...
// Cells containing a SUBTOTAL formula must be ignored.
Value func_subtotal(valVector args, ValueCalc *calc, FuncExtra *e)
{
    int function = calc->conv()->asInteger(args[0]).asInteger();
    Value range = args[1];
    int r1 = -1, c1 = -1, r2 = -1, c2 = -1;
    if (e) {
        r1 = e->ranges[1].row1;
        c1 = e->ranges[1].col1;
        r2 = e->ranges[1].row2;
        c2 = e->ranges[1].col2;
    }

    // exclude manually hidden rows. http://tools.oasis-open.org/issues/browse/OFFICE-2030
    bool excludeHiddenRows = false;
    if(function > 100) {
        excludeHiddenRows = true;
        function = function % 100; // translate e.g. 106 to 6.
    }

    // run through the cells in the selected range
    Value empty;
    if ((r1 > 0) && (c1 > 0) && (r2 > 0) && (c2 > 0)) {
        for (int r = r1; r <= r2; ++r) {
            const bool setAllEmpty = excludeHiddenRows && e->sheet->rowFormats()->isHidden(r);
            for (int c = c1; c <= c2; ++c) {
                // put an empty value to all cells in a hidden row
                if(setAllEmpty) {
                    range.setElement(c - c1, r - r1, empty);
                    continue;
                }
                Cell cell(e->sheet, c, r);
                // put an empty value to the place of all occurrences of the SUBTOTAL function
                if (!cell.isDefault() && cell.isFormula() && cell.userInput().indexOf("SUBTOTAL", 0, Qt::CaseInsensitive) != -1)
                    range.setElement(c - c1, r - r1, empty);
            }
        }
    }

    // Good. Now we can execute the necessary function on the range.
    Value res;
    QSharedPointer<Function> f;
    valVector a;
    switch (function) {
    case 1: // Average
        res = calc->avg(range, false);
        break;
    case 2: // Count
        res = Value(calc->count(range, false));
        break;
    case 3: // CountA
        res = Value(calc->count(range));
        break;
    case 4: // MAX
        res = calc->max(range, false);
        break;
    case 5: // Min
        res = calc->min(range, false);
        break;
    case 6: // Product
        res = calc->product(range, Value(0.0), false);
        break;
    case 7: // StDev
        res = calc->stddev(range, false);
        break;
    case 8: // StDevP
        res = calc->stddevP(range, false);
        break;
    case 9: // Sum
        res = calc->sum(range, false);
        break;
    case 10: // Var
        f = FunctionRepository::self()->function("VAR");
        if (!f) return Value::errorVALUE();
        a.resize(1);
        a[0] = range;
        res = f->exec(a, calc, 0);
        break;
    case 11: // VarP
        f = FunctionRepository::self()->function("VARP");
        if (!f) return Value::errorVALUE();
        a.resize(1);
        a[0] = range;
        res = f->exec(a, calc, 0);
        break;
    default:
        return Value::errorVALUE();
    }
    return res;
}

// Function: TRANSPOSE
Value func_transpose(valVector args, ValueCalc *calc, FuncExtra *)
{
    Q_UNUSED(calc);
    Value matrix = args[0];
    const int cols = matrix.columns();
    const int rows = matrix.rows();

    Value transpose(Value::Array);
    for (int row = 0; row < rows; ++row) {
        for (int col = 0; col < cols; ++col) {
            if (!matrix.element(col, row).isEmpty())
                transpose.setElement(row, col, matrix.element(col, row));
        }
    }
    return transpose;
}

/*
Commented out.
Absolutely no idea what this thing is supposed to do.
To anyone who would enable this code: it still uses koscript calls - you need
to convert it to the new style prior to uncommenting.

// Function: MULTIPLEOPERATIONS
Value func_multipleOP (valVector args, ValueCalc *calc, FuncExtra *)
{
  if (gCell)
  {
    context.setValue( new KSValue( ((Interpreter *) context.interpreter() )->cell()->value().asFloat() ) );
    return true;
  }

  gCell = ((Interpreter *) context.interpreter() )->cell();

  QValueList<KSValue::Ptr>& args = context.value()->listValue();
  QValueList<KSValue::Ptr>& extra = context.extraData()->listValue();

  if ( !KSUtil::checkArgumentsCount( context, 5, "MULTIPLEOPERATIONS", true ) )
  {
    gCell = 0;
    return false;
  }

  // 0: cell must contain formula with double/int result
  // 0, 1, 2, 3, 4: must contain integer/double
  for (int i = 0; i < 5; ++i)
  {
    if ( !KSUtil::checkType( context, args[i], KSValue::DoubleType, true ) )
    {
      gCell = 0;
      return false;
    }
  }

  //  ((Interpreter *) context.interpreter() )->document()->emitBeginOperation();

  double oldCol = args[1]->doubleValue();
  double oldRow = args[3]->doubleValue();
  debugSheets <<"Old values: Col:" << oldCol <<", Row:" << oldRow;

  Cell * cell;
  Sheet * sheet = ((Interpreter *) context.interpreter() )->sheet();

  Point point( extra[1]->stringValue() );
  Point point2( extra[3]->stringValue() );
  Point point3( extra[0]->stringValue() );

  if ( ( args[1]->doubleValue() != args[2]->doubleValue() )
       || ( args[3]->doubleValue() != args[4]->doubleValue() ) )
  {
    cell = Cell( sheet, point.pos.x(), point.pos.y() );
    cell->setValue( args[2]->doubleValue() );
    debugSheets <<"Setting value" << args[2]->doubleValue() <<" on cell" << point.pos.x()
              << ", " << point.pos.y() << endl;

    cell = Cell( sheet, point2.pos.x(), point.pos.y() );
    cell->setValue( args[4]->doubleValue() );
    debugSheets <<"Setting value" << args[4]->doubleValue() <<" on cell" << point2.pos.x()
              << ", " << point2.pos.y() << endl;
  }

  Cell * cell1 = Cell( sheet, point3.pos.x(), point3.pos.y() );
  cell1->calc( false );

  double d = cell1->value().asFloat();
  debugSheets <<"Cell:" << point3.pos.x() <<";" << point3.pos.y() <<" with value"
            << d << endl;

  debugSheets <<"Resetting old values";

  cell = Cell( sheet, point.pos.x(), point.pos.y() );
  cell->setValue( oldCol );

  cell = Cell( sheet, point2.pos.x(), point2.pos.y() );
  cell->setValue( oldRow );

  cell1->calc( false );

  // ((Interpreter *) context.interpreter() )->document()->emitEndOperation();

  context.setValue( new KSValue( (double) d ) );

  gCell = 0;
  return true;
}

*/

//AFA #include "math.moc"


#include <string>
#include <vector>
//#include <math.h>
#include <cmath>
#include <armadillo>

using namespace arma;

#include "tree_utils.h"
#include "tree.h"
#include "cont_models.h"
#include "constants.h" // for PI and E

void calc_vcv(Tree * tree, mat & vcv) {
    int numlvs = tree->getExternalNodeCount();
    vcv = mat(numlvs,numlvs);
    int count = 0;
    for (int i=0; i < numlvs; i++) {
        int count2 = 0;
        for (int j=0; j < numlvs; j++) {
            if (i != j) {
                Node * a = getMRCA_forVCV(tree->getExternalNode(i),tree->getExternalNode(j));
                double length = get_length_to_root(a);
                vcv(count,count2) = length;
            } else {
                double length = get_length_to_root(tree->getExternalNode(i));
                vcv(count,count2) = length;
            }
            count2 += 1;
        }
        count += 1;
    }
}

/*
 * get the MRCA
 * this calculates the typical algorithm for MRCA
 * can be a little slow, so probably best to use
 * getMRCAFromPath_forVCV
 */
Node * getMRCA_forVCV(Node * curn1,Node * curn2) {
    Node * mrca = NULL;
    //get path to root for first node
    vector<Node *> path1;
    Node * parent = curn1;
    path1.push_back(parent);
    while (parent != NULL) {
        path1.push_back(parent);
        if (parent->getParent() != NULL) {
            parent = parent->getParent();
        } else {
            break;
        }
    }
    //find first match between this node and the first one
    parent = curn2;
    bool x = true;
    while (x == true) {
        int psize = path1.size();
        for (int i = 0; i < psize; i++) {
            if (parent == path1.at(i)) {
                mrca = parent;
                x = false;
                break;
            }
        }
        parent = parent->getParent();
    }
    return mrca;
}

/*
 * this saves a great deal of time as it takes the already
 * obtained path and finds the match with the second node
 */

Node * getMRCAFromPath_forVCV(vector<Node *> * path1,Node * curn2) {
    Node * mrca = NULL;
    Node * parent = curn2;
    bool x = true;
    while (x == true) {
        int psize = path1->size();
        for (int i = 0; i < psize; i++) {
            if (parent == path1->at(i)) {
                mrca = parent;
                x = false;
                break;
            }
        }
        parent = parent->getParent();
    }
    return mrca;
}

/*
   calculate the maximum likelihood for the ancestral state and the rate
   calc_bm_like only calculates the rate and solves for the anc state
#http://en.wikipedia.org/wiki/Multivariate_normal_distribution#Non-degenerate_case
x should be a vector
mu should be a vector
sigma should be vcv with sigma already applied
 */
double norm_pdf_multivariate(rowvec & x, rowvec & mu, mat & sigma) {
    unsigned int size = x.n_cols;
    if (size == mu.n_cols && sigma.n_rows == size && sigma.n_cols == size) {
        double DET = det(sigma);
        if (DET == 0) {
            cerr << "The covariance matrix can't be singular" << endl;
            exit(0);
        }
        double norm_const = 1.0/ ( pow((2*PI),double(size)/2) * pow(DET,1.0/2) );
        mat x_mu = (mat) x - mu;
        mat tm = (x_mu * inv(sigma) * trans(x_mu));
        double big1 = tm(0,0);
        double result = pow(E, -0.5 * big1);
        double final = norm_const * result;
        return final;
    } else {
        cerr << "The dimensions of the input don't match" << endl;
        exit(0);
    }
}

double norm_log_pdf_multivariate(rowvec & x, rowvec & mu, mat & sigma) {
    unsigned int size = x.n_cols;
    if (size == mu.n_cols && sigma.n_rows == size && sigma.n_cols == size) {
        double DET;
        double sign;
        log_det(DET,sign,sigma);
        if (DET == 0) {
            cerr << "The covariance matrix can't be singular" << endl;
            exit(0);
        }
        mat U; vec s; mat V;
        svd(U, s, V, sigma,"dc");
        mat diagD (s.size(),s.size());
        diagD.zeros();
        diagD.diag() = 1./s;
        mat invC2 = V*diagD*trans(U);
        rowvec ancA = x - mu;
        double final = -.5 * (dot(invC2*trans(ancA),ancA))-0.5 * DET -0.5 * (size * log(2*PI));
        return final;
    } else {
        cerr << "The dimensions of the input don't match" << endl;
        exit(0);
    }
}

/**
 * assumes that the characters are in get_cont_char and that the
 * results will be in assocDoubleVector as val and valse
 */
void calc_square_change_anc_states(Tree * tree, int index) {
    int df = 0;
    int count = 0;
    map<Node *,int> nodenum;
    for (int i=0; i < tree->getInternalNodeCount(); i++) {
        nodenum[tree->getInternalNode(i)] = count;
        count += 1;
        df += 1;
        (*tree->getInternalNode(i)->getDoubleVector("val"))[index] = 0.0;
    }
    df -= 1;
    mat fullMcp(df+1,df+1);
    vec fullVcp(df+1);
    fullMcp.fill(0.0);
    fullVcp.fill(0.0);
    calc_postorder_square_change(tree->getRoot(),nodenum,&fullMcp,&fullVcp,index);
    mat b = chol(fullMcp);
    vec mle;
    mat x = solve(trimatl(b.t())*b,fullVcp);
    count = 0;
    for (int i=0; i < tree->getInternalNodeCount(); i++) {
        (*tree->getInternalNode(i)->getDoubleVector("val"))[index] = x(nodenum[tree->getInternalNode(i)],0);
        count += 1;
    }
}

void calc_postorder_square_change(Node * node,map<Node *,int> & nodenum,
    mat * fullMcp, mat * fullVcp, int index) {
    for (int i=0; i < node->getChildCount(); i++) {
        calc_postorder_square_change(node->getChild(i),nodenum,fullMcp,fullVcp,index);
    }
    if (node->getChildCount() > 0) {
        int nni = nodenum[node];
        for (int j=0; j < node->getChildCount(); j++) {
            double tbl = 2./node->getChild(j)->getBL();
            (*fullMcp)(nni,nni) += tbl;
            if (node->getChild(j)->getChildCount() == 0) {
                (*fullVcp)[nni] += (*node->getChild(j)->getDoubleVector("val"))[index] * tbl;
            } else {
                int nnj = nodenum[node->getChild(j)];
                (*fullMcp)(nni,nnj) -= tbl;
                (*fullMcp)(nnj,nni) -= tbl;
                (*fullMcp)(nnj,nnj) += tbl;
            }
        }
    }
}

double calc_bm_node_postorder(Node * node, int nch, double sigma){
    double node_like = 0.;
    for (int i=0;i<node->getChildCount();i++){
        if(node->getChild(i)->isInternal()){
           node_like += calc_bm_node_postorder(node->getChild(i),nch,sigma);
        }
    }
    if (node->isInternal()){
        double ch1 = (*node->getChild(0)->getDoubleVector("val"))[nch];
        double ch2 = (*node->getChild(1)->getDoubleVector("val"))[nch];
        double ch = ch1 - ch2;
        double bl1 = node->getChild(0)->getBL();
        double bl2 = node->getChild(1)->getBL();
        double bl = bl1 + bl2;
        double cur_like = ((-0.5)* ((log(2*M_PI*sigma))+(log(bl))+(pow(ch,2)/(sigma*bl))));
        node_like += cur_like;
        if (node->isRoot() == false){
            (*node->getDoubleVector("val"))[nch] = ((bl2*ch1)+(bl1*ch2))/(bl);
            node->setBL(node->getBL()+((bl1*bl2)/(bl1+bl2)));
        }
    }
    return node_like;
}

double calc_bm_prune(Tree * tr, double sigma){
    int nchar = (*tr->getRoot()->getDoubleVector("val")).size();
    double tlike = 0;
    map<Node *, double> oldlen;
    for (int i=0;i<tr->getNodeCount();i++){oldlen[tr->getNode(i)] = tr->getNode(i)->getBL();}
    for (int i=0;i<nchar;i++){
        for (int j=0;j<tr->getNodeCount();j++){tr->getNode(j)->setBL(oldlen[tr->getNode(j)]);}
        tlike += calc_bm_node_postorder(tr->getRoot(),i,sigma);
    }
    return tlike;
}


#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <vector>
#include <algorithm>
#include <math.h>

#include "NRGclasses.hpp"
#include "NRGfunctions.hpp"
using namespace std;

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

int CNRGarray::NumBlocks(){

  return((int)(QNumbers.size()/NQNumbers));

}

//////////////
double CNRGarray::EniBlockj(int i, int j){

  // returns the energy of the i-th state in block j

  double aux=0.0;

  if ( (2*j-2)<BlockBegEnd.size() )
    {
      if ( (BlockBegEnd[2*j-2]+(i-1))<dEn.size() )
	{
	  aux=dEn[BlockBegEnd[2*j-2]+(i-1)];
	}
    }

  return(aux);

}
//////////////

//////////////
void CNRGarray::PrintQNumbers(){

  int ii;
  cout << "Qnumbers : " << endl;
  for (ii=0;ii<QNumbers.size();ii++)
    {
      cout << fixed << QNumbers[ii] << "  ";
      if ((ii+1) % NQNumbers == 0) cout << endl;
    }
  cout << endl;
  cout << resetiosflags (ios_base::floatfield);

}

//////////////
void CNRGarray::PrintEn(){

  int ii;
  int iblock,pos_in_block;
  for (ii=0;ii<dEn.size();ii++){
    iblock=GetBlockFromSt(ii,pos_in_block);
    cout << "En = " << dEn[ii] << " ---| " ;
    for (int iQn=0;iQn<NQNumbers;iQn++){
      cout << GetQNumber(iblock,iQn) << "  ";
    }
    if (ii<Kept.size()){if(Kept[ii]){cout << "(K)";}}
    cout << ">_N= ";
    cout << Nshell << endl;
  }
}

//////////////
void CNRGarray::PrintBlockQNumbers(int iblock){

  cout << "Block no. " << iblock << " : ";
  for (int ii=0;ii<NQNumbers;ii++)
    cout << fixed << GetQNumber(iblock,ii) << " ";
  cout << endl;
  cout << resetiosflags (ios_base::floatfield);

}

//////////////
void CNRGarray::PrintBlockEn(int iblock){

  PrintBlockQNumbers(iblock);

  int iBeg=GetBlockLimit(iblock,0);
  int iEnd=GetBlockLimit(iblock,1);
  cout << " dEn : " << endl;
  if (dEn.size()>0)
    {
      for (int ii=iBeg;ii<=iEnd;ii++)
	cout << ii << " : " << scientific << dEn[ii] << endl;
    }
  //end if
  cout << resetiosflags (ios_base::floatfield);
  cout << "Kept States: "<< endl;
  if (Kept.size()>0){
    for (int ii=iBeg;ii<=iEnd;ii++){
      cout << Kept[ii] << " ";
    }
    cout << endl;
  }
  // end if

}

//////////////
void CNRGarray::PrintBlock(int iblock){

  PrintBlockEn(iblock);

  int iBeg=GetBlockLimitEigv(iblock,0);
  int iEnd=GetBlockLimitEigv(iblock,1);

  int NstBl=GetBlockLimit(iblock,1)-GetBlockLimit(iblock,0)+1;
  cout << " dEigVec (rows): " << endl;;
  if (dEigVec.size()>0){
    int i1=1;
    for (int ii=iBeg;ii<=iEnd;ii++){
      cout << scientific << dEigVec[ii] << "  ";
      if (i1%NstBl==0) cout << endl;
//       // complex
//       cout << scientific << cEigVec[ii] << "  ";
//       if (i1%NstBl==0) cout << endl;
      i1++;
    }
  } else if (cEigVec.size()>0){
    int i1=1;
    for (int ii=iBeg;ii<=iEnd;ii++){
      // complex
      cout << scientific << cEigVec[ii] << "  ";
      if (i1%NstBl==0) cout << endl;
      i1++;
    }
  }
  //end if

  cout << resetiosflags (ios_base::floatfield);

}

//////////////
void CNRGarray::PrintAll(){

  int ii;
  //void CNRGarray::PrintQNumbers();

  PrintQNumbers();

  cout << " Any totalS QNs? = " << totalS << endl;
  if (Sqnumbers.size()>0){
    cout << "  totalS QNs positions : ";
    for (ii=0;ii<Sqnumbers.size();ii++)
      cout << Sqnumbers[ii] << " ";
    cout << endl;
  }
  cout << "dEn : " << endl;
  for (ii=0;ii<dEn.size();ii++)
    cout << scientific << dEn[ii] << " ";
  cout << endl;

  cout << "dEigVec (rows): " << endl;
  for (ii=0;ii<dEigVec.size();ii++)
    cout << scientific << dEigVec[ii] << " ";
  cout << endl;
  for (ii=0;ii<cEigVec.size();ii++)
    cout << scientific << cEigVec[ii] << " ";
  cout << endl;

  cout << "BlockBegEnd : " << endl;
  for (ii=0;ii<BlockBegEnd.size();ii+=2)
    cout << BlockBegEnd[ii] << " "<< BlockBegEnd[ii+1] << ", ";
  cout << endl;

  cout << "Size Blocks : " << endl;
  for (ii=0;ii<NumBlocks();ii++)
    cout << GetBlockSize(ii) << " ";
  cout << endl;

  cout << "Child States (size= "<< ChildStates.size() <<" ):"<< endl;
  // Why clear?
  //ChildStates.clear();
  if (ChildStates.size()>0){
    for (ii=0;ii<ChildStates.size();ii++){
      if (ChildStates[ii].size()>0){
	for (int jj=0;jj<ChildStates[ii].size();jj++){
	  cout << ChildStates[ii][jj] << " ";
	}
      cout << endl;
      }
      // end if
    }
    cout << endl;
  }
  // end if

  cout << "Kept States (size= "<< Kept.size() <<" ):"<< endl;
  if (Kept.size()>0){
    for (ii=0;ii<Kept.size();ii++){
      cout << Kept[ii] << " ";
    }
    cout << endl;
  }
  // end if

  cout << resetiosflags (ios_base::floatfield);
}
//////////////
int CNRGarray::Nstates(){

  // Changing this:
  //return(BlockBegEnd[NQNumbers*(NumBlocks()-1)+1]-BlockBegEnd[0]+1);
  return(BlockBegEnd.back()-BlockBegEnd.front()+1);

}

//////////////
void CNRGarray::SetE0zero(){

  double Emin=*min_element(dEn.begin(),dEn.end());
  cout <<  "Emin = " << Emin << endl;

  for (int ii=0;ii<dEn.size();ii++) dEn[ii]-=Emin;

}

//////////////
void CNRGarray::SetDegen(){

  // int nst,ii;

  // Search for near-degeneracies in the spectrum
  // and set the energy of the states to be the same
  int ii=0;
  while ( ii<dEn.size() ){
    for (int jj=ii+1;jj<dEn.size();jj++){
//     if (fabs(dEn[ii]-dEn[ii-1])<1.0E-10) nst=iDegen[ii-1]+1;
//     else nst=1;
//     iDegen.push_back(nst);
      if (dEqualPrec(dEn[jj],dEn[ii],MyEPS)){
	if (dEn[jj]>dEn[ii])
	  dEn[jj]=dEn[ii];
	else
	  dEn[ii]=dEn[jj];
      }
      //end if equal
    }
    // end for loop in jj
    ii++;
  }
  // end while loop
}

///////////////////////
void CNRGarray::FilterQNumbers(){

  int equal=0;
  int ii,i1;

  vector<double>::iterator qnums_iter,qnums_iter2,qnums_ii1,qnums_ii2;

  for (qnums_iter=QNumbers.begin(); qnums_iter<QNumbers.end();
       qnums_iter+=NQNumbers)
    {

      //cout << " QN1 = " << (*qnums_iter) << " , " << *(qnums_iter+1)<< endl;
      ii=1;
      qnums_iter2=qnums_iter+ii*NQNumbers;
      while (qnums_iter2<QNumbers.end())
	{
	  //cout << " QN2 = " << (*qnums_iter2) << " , " << *(qnums_iter2+1)<< endl;
	  equal=1;
	  i1=0;
	  for (qnums_ii2=qnums_iter2;
	       qnums_ii2<qnums_iter2+NQNumbers;qnums_ii2++)
	    {
	      qnums_ii1=qnums_iter+i1;
	      if ( (*qnums_ii2==*qnums_ii1)&&(equal==1) ) equal=1;
	      else equal=0;
	      i1++;
	    }
	  if (equal==1)
	    {
	      //cout << "Found equal " << endl;
	      QNumbers.erase(qnums_iter2,qnums_iter2+NQNumbers);
	      ii--;
	    }
	  ii++;
	  qnums_iter2=qnums_iter+ii*NQNumbers;
	}
    }

}

//////////////
void CNRGarray::ClearAll(){

  QNumbers.clear();
  dEn.clear();
  dEigVec.clear();
  iDegen.clear();

  BlockBegEnd.clear();

  ChildStates.clear();

  Kept.clear();

}

//////////////
double CNRGarray::GetQNumber(int iblock, int whichqn){

  int iq=iblock*NQNumbers+whichqn;

  if (QNumbers.size()>0)
     return(QNumbers[iq]);
  else
     return(0.0);

}

//////////////
int CNRGarray::GetBlockLimit(int iblock, int whichlimit){

  if(iblock<0)
    return(0);
  else
    return(BlockBegEnd[2*iblock+whichlimit]);

}

//////////////
int CNRGarray::GetBlockSize(int iblock){

  return(BlockBegEnd[2*iblock+1]-BlockBegEnd[2*iblock]+1);

}

//////////////
int CNRGarray::GetBlockSize(int iblock, bool kp){

  int Nstkp=0;

  if (Kept.size()<=BlockBegEnd[2*iblock+1]) return(0);

  for (int ii=BlockBegEnd[2*iblock];ii<=BlockBegEnd[2*iblock+1];ii++){

    if (Kept[ii]==kp) Nstkp++;

  }

  return(Nstkp);

}

//////////////
int CNRGarray::GetBlockFromSt(int ist,int &pos_in_block){

  // Find which block ist belongs to:
  // FInds iblock such that
  // BlockBegEnd[2*iblock]<=ist<=BlockBegEnd[2*iblock+1]

  int iblock=0;

  while (ist>BlockBegEnd[2*iblock+1]) {iblock++;}

  pos_in_block=ist-BlockBegEnd[2*iblock];

  // if state is not there (holes in BlockBegEnd, pos_in_block is negative.

  return(iblock);
}
/////////////
int CNRGarray::GetBlockLimitEigv(int iblock, int whichlimit){

  // Returns position in dEigVec corresponding to block iblock

  int posEig=0;
  int NstBl=0;

  for (int ii=0;ii<iblock;ii++)
    {
      NstBl=GetBlockSize(ii);
      posEig+=NstBl*NstBl;
    }

  NstBl=GetBlockSize(iblock);
  posEig+=whichlimit*(NstBl*NstBl - 1);

  return(posEig);

}

/////////////
double CNRGarray::GetBlockEmin(int iblock){

  double Emin = *min_element(dEn.begin()+GetBlockLimit(iblock,0),
			     dEn.begin()+GetBlockLimit(iblock,1)+1);

  return(Emin);
}

/////////////
double CNRGarray::GetBlockEmax(int iblock){

  double Emax = *max_element(
			     dEn.begin()+GetBlockLimit(iblock,0),
			     dEn.begin()+GetBlockLimit(iblock,1)+1);

  return(Emax);
}

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

// bool GTEcut (double E, double Ecut) {
//   return (E>Ecut);
// }

int CNRGarray::GetBlockEcutPos(int iblock,double Ecut){

  vector<double>::iterator FindEnGTEcut=dEn.begin()+GetBlockLimit(iblock,0);

   while ( (*FindEnGTEcut<=Ecut)
 	  &&(FindEnGTEcut<=dEn.begin()+GetBlockLimit(iblock,1)) )
//  to allow close-by states to be retained (did not work)
//   while ( ( dLTPrec(*FindEnGTEcut,Ecut,0.01/fabs(Ecut)) )
// 	  &&(FindEnGTEcut<=dEn.begin()+GetBlockLimit(iblock,1)) )
    {FindEnGTEcut++;}

//   =find_if(dEn.begin()+GetBlockLimit(iblock,0),
// 	      dEn.begin()+GetBlockLimit(iblock,1),
// 	      GTEcut); // doesnt work

  return((int)(FindEnGTEcut-dEn.begin()));
}

/////////////
void CNRGarray::SetKept(int Ncutoff){

  // New (2019)

  SetDegen();

  //

  double dEcut=Ecut(Ncutoff);

  Kept.clear();
  vector<double>::iterator it;

  cout << "SetKept: Ncutoff = " << Ncutoff
       << " Ecut = " << dEcut << endl;

  int Nkept=0;
  int Ndisc=0;

  for (it=dEn.begin();it<dEn.end();it++){
    if ((*it)<=dEcut){
      Kept.push_back(true); Nkept++;
    }else{Kept.push_back(false);Ndisc++;}

  }

  cout << "SetKept: Nkept = " << Nkept << " Ndisc = " << Ndisc << endl;

}
////////////
bool CNRGarray::CheckKept(int ist, bool kp){
  //
  // Checks if Keep[ist] matches kp

  if (Kept.size()<=ist){return(false);}
  else{
    if (Kept[ist]==kp) return(true);
    else return(false);
  }
}

bool CNRGarray::CheckKept(int iblock, int istbl, bool kp){

  int ist=GetBlockLimit(iblock,0)+istbl;

  return(CheckKept(ist,kp));
}

/////////////
int CNRGarray::GetBlockFromQNumbers(double *qnums){

  // uses predicate "dEqual" defined NRGfunctions.hpp

  vector <double>::iterator it=QNumbers.begin()-1;
  bool ok=false;

  // watch out for false alarms
  while (!ok)
   {
  it = search (it+1, QNumbers.end(), qnums, qnums+NQNumbers,dEqual);

    ok=(((int)(it-QNumbers.begin()))%NQNumbers==0)||((int)(it-QNumbers.end())==0)?true:false;
   }

  //return (int(it-QNumbers.begin())/NQNumbers);
  // Adding safeguards
  int ibl=(int)(it-QNumbers.begin())/NQNumbers;
  if (ibl>NumBlocks()-1){
    //cout << "GetBlockFromQNumbers: ibl larger than NumBlocks()-1. Returning -1" << endl;
    ibl=-1;
  }

  return (ibl);

  // Adding safeguards

}

///////////// April 08
double CNRGarray::GetQNumberFromSt(int ist, int whichqn){

  int pos=0;
  int ibl=GetBlockFromSt(ist,pos);

  return(GetQNumber(ibl,whichqn));

}
/////////////
boost::numeric::ublas::matrix<double> CNRGarray::EigVec2BLAS(int iblock){

  // Converts the section in dEigVec corresponding to block iblock to uBLAS
  // Format: each eigenvector in a ROW!

  int Nst=GetBlockSize(iblock);
  int pos=GetBlockLimitEigv(iblock,0);

  //cout << "NstBlock = " << Nst << "  Pos in dEigVec = " << pos << endl;

  boost::numeric::ublas::matrix<double> Maux(Nst,Nst);

//   copy(dEigVec.begin()+pos,dEigVec.begin()+pos+Nst*Nst,
// 	   Maux.begin2());

  // Let me try this:

  vector<double>::iterator it;

  it=dEigVec.begin()+pos;
  for (int ii=0;ii<Nst;ii++)
    {
      for (int jj=0;jj<Nst;jj++)
	{
	  Maux(ii,jj)=*it;
	  it++;
	}
    }

  //return(boost::numeric::ublas::trans(Maux)); // In Columns
  return(Maux);          // In Rows

}
/////////////
boost::numeric::ublas::matrix<complex<double> > CNRGarray::cEigVec2BLAS(int iblock){

  // Converts the section in cEigVec corresponding to block iblock to uBLAS
  // Format: each eigenvector in a ROW! COMPLEX MATRIX

  int Nst=GetBlockSize(iblock);
  int pos=GetBlockLimitEigv(iblock,0);

  //cout << "NstBlock = " << Nst << "  Pos in dEigVec = " << pos << endl;

  boost::numeric::ublas::matrix<complex<double> > Maux(Nst,Nst);

//   copy(dEigVec.begin()+pos,dEigVec.begin()+pos+Nst*Nst,
// 	   Maux.begin2());

  // Let me try this:

  vector<complex<double> >::iterator it;

  it=cEigVec.begin()+pos;
  for (int ii=0;ii<Nst;ii++)
    {
      for (int jj=0;jj<Nst;jj++)
	{
	  Maux(ii,jj)=*it;
	  it++;
	}
    }

  //return(boost::numeric::ublas::trans(Maux)); // In Columns
  return(Maux);          // In Rows

}
///////

///////////// April 09
double CNRGarray::GetEigVecComponent(int ist,int istbasis){

  // For "uncut" vectors only!
  // A CNRGbasisarray version for this one and for
  // GetBlockLimitEigv should do the job
  // in cut objects.
  //

  if (dEigVec.size()==0){
    cout << "GetEigVecComponent error: No dEigVec. Return 0." << endl;
    return(0.0);
  }

  int posBl;
  int posBlbasis;
  int ibl=GetBlockFromSt(ist,posBl);
  int ibl_basis=GetBlockFromSt(istbasis,posBlbasis);
  int bl_size=GetBlockSize(ibl);
  int ieigv0=GetBlockLimitEigv(ibl,0);

  if (ibl==ibl_basis){
    vector<double>::iterator it;
    it=dEigVec.begin()+ieigv0+posBl*bl_size+posBlbasis;

    return(*it);
  }
  else{return(0.0);}

}

/////////////
double CNRGarray::Ecut(int Ncut){

  vector<double> Eaux=dEn;
  double aux,aux2;

  sort(Eaux.begin(),Eaux.end());

  //  if (Ncut>Eaux.size()) aux=Eaux.back();
  //  else aux=Eaux[Ncut];

// Let's try this
  if ( Ncut>=(Eaux.size()-1) ) aux=Eaux.back();
  else{
    aux=Eaux[Ncut-1]; // state Ncut
    aux2=Eaux[Ncut];  // state Ncut+1
    cout << " CNRGarray::Ecut : Original Ecut = "<< aux
	 << " Ecut+1 = " << aux2 << endl;
    //check for near-degenerate levels
    int ii=1;
//     while ( (aux2>0.0)&&(fabs((aux2-aux)/aux)<0.001)
    while ( (aux2>0.0)&&(fabs((aux2-aux)/aux)<0.01)
	    &&(ii<Eaux.size()) ){aux2=Eaux[Ncut+ii];ii++;}
    if (ii>1) {aux=Eaux[Ncut+ii-2];aux2=Eaux[Ncut+ii-1];}
    cout << " CNRGarray::Ecut : New Ecut = "<< aux
	 << " New Ecut+1 = " << aux2
         << " Nkept = " << Ncut+ii-1 << endl;
  }

  return(aux);
}

///////////// May 08
int CNRGarray::GetiGS(){

  // returns *position* of the minimum element in dEn

  vector<double>::iterator it=min_element(dEn.begin(),dEn.end());

  int iGS=0;
  while (it>dEn.begin()){it--;iGS++;}

  return(iGS);

}

void CNRGarray::SaveQSParameters(){

  cout << "Saving in old format " << endl;

  // outstream
  ofstream OutFile;
  char arqname[32];
  char CNsites[8];

  sprintf(CNsites,"%d",Nshell);

  //
  // Saving QS parameters
  //

  strcpy(arqname,"DataQSN");
  strcat(arqname,CNsites);
  strcat(arqname,".dat");

  cout << "File is " << arqname << endl;

  OutFile.open(arqname);

  int iGS=GetiGS();
  int pos=0;
  int blockGS=GetBlockFromSt(iGS,pos);

  cout << " Nsites " << Nshell+1 << " Nstates " << Nstates()
       << " iGS = " << iGS << " BlockGS = " << blockGS
       << " NumQSblocks = " << NumBlocks()
       << endl;

  OutFile << Nshell+1 << " " << Nstates()
       << " " << iGS << " " << blockGS
       << " " << NumBlocks()
       << endl;

  for (int iblock=0;iblock<NumBlocks();iblock++)
    {
      OutFile << fixed << GetQNumber(iblock,0) << "  " << GetQNumber(iblock,1) << endl;
    }
  OutFile << resetiosflags (ios_base::floatfield);
  for (int iblock=0;iblock<NumBlocks();iblock++)
    {
      OutFile << GetBlockLimit(iblock,0) << "  " << GetBlockLimit(iblock,1) << endl;
    }

  OutFile.close();

  //
  // Saving Energies
  //

  strcpy(arqname,"DataEQSN");
  strcat(arqname,CNsites);
  strcat(arqname,".bin");

  // Using C format...
  FILE *pFile;

  pFile=fopen(arqname,"wb");
  fwrite (&dEn[0], sizeof(double), dEn.size() ,pFile);
  fclose(pFile);

}
//

void CNRGarray::SaveBin(char arqname[]){

  vector<double>::iterator dit;
  vector<int>::iterator iit;
  vector<bool>::iterator boolit;
  vector<complex<double> >::iterator cit;

  ofstream OutFile;
  double daux;
  int iaux;
  bool auxbool;

  complex<double> caux;

  OutFile.open(arqname, ios::out | ios::binary);

  if (!OutFile){cout << "SaveBin: Cannot save data in " << arqname << endl; return;}

  // Save Nshell
  OutFile.write((char*)&Nshell, sizeof(int));
  // Save NQNumbers
  OutFile.write((char*)&NQNumbers, sizeof(int));
  // Save QNumbers
  iaux=QNumbers.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(dit=QNumbers.begin();dit<QNumbers.end();dit++){
    daux=*dit;
    OutFile.write(reinterpret_cast<char*>(&daux), sizeof(double));
  }
  // Save dEn
  iaux=dEn.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(dit=dEn.begin();dit<dEn.end();dit++){
    // (&(*dit)) is weird but it is right...
    OutFile.write(reinterpret_cast<char*>(&(*dit)), sizeof(double));
  }
  // Save dEigVec
  iaux=dEigVec.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(dit=dEigVec.begin();dit<dEigVec.end();dit++){
    // (&(*dit)) is weird but it is right...
    OutFile.write(reinterpret_cast<char*>(&(*dit)), sizeof(double));
  }
  // Save cEigVec
  iaux=cEigVec.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(cit=cEigVec.begin();cit<cEigVec.end();cit++){
    // (&(*cit)) is weird but it is right...
    OutFile.write(reinterpret_cast<char*>(&(*cit)), sizeof(complex<double> ));
  }
  // Save BlockBegEnd
  iaux=BlockBegEnd.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(iit=BlockBegEnd.begin();iit<BlockBegEnd.end();iit++){
    OutFile.write(reinterpret_cast<char*>(&(*iit)), sizeof(int));
  }
  // Save iDegen
  iaux=iDegen.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(iit=iDegen.begin();iit<iDegen.end();iit++){
    OutFile.write(reinterpret_cast<char*>(&(*iit)), sizeof(int));
  }
  // Save Kept
  iaux=Kept.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(boolit=Kept.begin();boolit<Kept.end();boolit++){
    //OutFile.write(reinterpret_cast<char*>(&(*boolit)), sizeof(bool));
    auxbool=*boolit;
    OutFile.write((char*)&auxbool, sizeof(bool));
  }
  // Save totalS,Sqnumbers
  OutFile.write((char*)&totalS, sizeof(bool));
  iaux=Sqnumbers.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(iit=Sqnumbers.begin();iit<Sqnumbers.end();iit++){
    OutFile.write(reinterpret_cast<char*>(&(*iit)), sizeof(int));
  }

  OutFile.close();

  if (!OutFile.good()){cout << "SaveBin: error saving" << endl;}

}
///

void CNRGarray::ReadBin(char arqname[]){

  // outstream
  ifstream InFile;

  InFile.open(arqname, ios::in | ios::binary);

  ReadBinInStream(InFile);

  InFile.close();

  if (!InFile.good()){cout << "ReadBin: error reading" << endl;}

}
///

void CNRGarray::ReadBinInStream(ifstream &InFile){

  int isize,iaux, iaux2;
  double daux;
  bool boolaux;
  complex<double> caux;

  // Read Nshell
  InFile.read((char*)&Nshell, sizeof(int));
  // Read NQNumbers
  InFile.read((char*)&NQNumbers, sizeof(int));
  // Read QNumbers
  QNumbers.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&daux), sizeof(double));
    QNumbers.push_back(daux);
  }
  // Read dEn
  dEn.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&daux), sizeof(double));
    dEn.push_back(daux);
  }
  // Read dEigVec
  dEigVec.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&daux), sizeof(double));
    dEigVec.push_back(daux);
  }
  // Read cEigVec
  cEigVec.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&caux), sizeof(complex<double> ));
    cEigVec.push_back(caux);
  }
  // Read BlockBegEnd
  BlockBegEnd.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&iaux2), sizeof(int));
    BlockBegEnd.push_back(iaux2);
  }
  // Read iDegen
  iDegen.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&iaux2), sizeof(int));
    iDegen.push_back(iaux2);
  }
  // Read Kept
  Kept.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    //InFile.read(reinterpret_cast<char*>(&boolaux), sizeof(bool));
    InFile.read((char*)(&boolaux), sizeof(bool));
    Kept.push_back(boolaux);
  }
  // Read totalS,Sqnumbers
  InFile.read((char*)&totalS, sizeof(bool));
  Sqnumbers.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&iaux2), sizeof(int));
    Sqnumbers.push_back(iaux2);
  }

}

//////////////
double CNRGarray::PartitionFuncTeq0(){

  // Works only for Q,Sz basis (not anymore)
  // 04/2010: Including 2S+1 degeneracy factor for SU(2) symmetries
  //

  double sum=0.0;

//   vector<double>::iterator dit;

//   for (dit=dEn.begin();dit<dEn.end();dit++){
//     if (dEqualPrec(abs((*dit)),0.0,1e-10)) sum+=1.0;
//   }

  for (int ibl=0;ibl<NumBlocks();ibl++){
    double Si=0.0;
    if (totalS){
      Si=GetQNumber(ibl,Sqnumbers[0]); // only a single SU(2) for now
    }

    for (int ist=GetBlockLimit(ibl,0);ist<=GetBlockLimit(ibl,1);ist++){
      if ( dEqualPrec(abs(dEn[ist]),0.0,1e-10) ) sum+=2.0*Si+1.0;
    }
    // end loop in states
  }
  // end loop in blocks

//
 return(sum);

}

//////////////
double CNRGarray::PartitionFunc(double betabar){

  // Works only for Q,Sz basis (not anymore)
  // 04/2010: Including 2S+1 degeneracy factor for SU(2) symmetries
  //

  double sum=0.0;

//   vector<double>::iterator dit;

//   for (dit=dEn.begin();dit<dEn.end();dit++){
//     sum+=exp(-betabar*(*dit));
//   }

  for (int ibl=0;ibl<NumBlocks();ibl++){
    double Si=0.0;
    if (totalS){
      Si=GetQNumber(ibl,Sqnumbers[0]); // only a single SU(2) for now
    }

    for (int ist=GetBlockLimit(ibl,0);ist<=GetBlockLimit(ibl,1);ist++){
      sum+=(2.0*Si+1.0)*exp(-betabar*(dEn[ist]));
    }
    // end loop in states
  }
  // end loop in blocks

  return(sum);
}

//////////////
//////////////
double CNRGarray::PartitionFuncDisc(double betabar){

  double sum=0.0;

  if (Kept.size()==0){
    cout << "PartitionFuncDisc: Kept not defined. Returning -1.0 " << endl;
    return(-1.0);
  }

  for (int ibl=0;ibl<NumBlocks();ibl++){
    double Si=0.0;
    if (totalS){
      Si=GetQNumber(ibl,Sqnumbers[0]); // only a single SU(2) for now
    }

    for (int ist=GetBlockLimit(ibl,0);ist<=GetBlockLimit(ibl,1);ist++){
      if (Kept[ist]==false)
	sum+=(2.0*Si+1.0)*exp(-betabar*(dEn[ist]));
    }
    // end loop in states
  }
  // end loop in blocks

  return(sum);
}

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

void CNRGarray::SetEigVecToOne(){

  // Sets eigenvectors to (1 0 0, 0 1 0, 0 0 1... on each block in the basis)
  // Creates an "uncut" dEigVec with Nbl x Nbl block components
  dEigVec.clear();

  for(int ibl=0;ibl<NumBlocks();ibl++){
    // within each block
    int ist0=GetBlockLimit(ibl,0);
    int istF=GetBlockLimit(ibl,1);
    for (int ist1=ist0;ist1<=istF;ist1++){
      for (int ist2=ist0;ist2<=istF;ist2++){
	if (ist1==ist2)
	  dEigVec.push_back(1.0); // diagonal
	else
	  dEigVec.push_back(0.0); // off-diagonal
      }
    }
    // end loops in block states
  }
  // end block loops

}

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

boost::numeric::ublas::matrix<double> CNRGarray::ExpEi2BLAS(int iblock, double betabar){

  int Nst1=GetBlockSize(iblock);

  int ist=GetBlockLimit(iblock,0);

  // NO NEED!
//   double Si=0.0;
//   if (totalS){
//     Si=GetQNumber(iblock,Sqnumbers[0]);
//     // only a single SU(2) for now
//   }

  boost::numeric::ublas::matrix<double> dMatAux(Nst1,Nst1);

  for (int ii=0;ii<Nst1;ii++){
    for (int jj=0;jj<Nst1;jj++){
      if (ii==jj)
	// WRONG
// 	dMatAux(ii,jj)=(2.0*Si+1.0)*exp(-betabar*dEn[ist]);
	dMatAux(ii,jj)=exp(-betabar*dEn[ist]);
      else dMatAux(ii,jj)=0.0;
    }
    ist++;
  }
  // end loop

  return(dMatAux);

}
//

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

bool CNRGarray::CheckComplex(){

  bool result=false;

  if ( (cEigVec.size()>0)&&(dEigVec.size()==0) ) result=true;

  return(result);

}

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

//////////////////////////////////////////////////
//////////////////////////////////////////////////
//                                              //
//                                              //
//     Class CNRGbasisarray functions           //
//                                              //
//                                              //
//////////////////////////////////////////////////
//////////////////////////////////////////////////

//////////////
//void CNRGbasisarray::ClearVecsBasis(){
void CNRGbasisarray::ClearAll(){

  CNRGarray::ClearAll();

  iType.clear();
  StCameFrom.clear();
  BlockBegEndBC.clear();
  BlockBegEndEigVec.clear();

  StCameFromQNumbers.clear();

}
//////////////
void CNRGbasisarray::PrintAll(){

  CNRGarray::PrintAll();

  cout << "Type : " << endl;
  for (int ii=0;ii<iType.size();ii++)
    cout << iType[ii] << " ";
  cout << endl;

  cout << "StCameFrom : " << endl;
  for (int ii=0;ii<StCameFrom.size();ii++)
    cout << StCameFrom[ii] << " ";
  cout << endl;

  cout << "iDegen : " << endl;
  for (int ii=0;ii<iDegen.size();ii++)
    cout << iDegen[ii] << " ";
  cout << endl;

  cout << "BlockBegEndBC : " << endl;
  for (int ii=0;ii<BlockBegEndBC.size();ii+=2)
    cout << BlockBegEndBC[ii] << " "<< BlockBegEndBC[ii+1] << ", ";
  cout << endl;

  cout << "BlockBegEndEigVec : " << endl;
  for (int ii=0;ii<BlockBegEndEigVec.size();ii++)
    cout << BlockBegEndEigVec[ii] << " ";
  cout << endl;

  cout << "StCameFromQNumbers : " << endl;
  for (int ii=0;ii<StCameFromQNumbers.size();ii++)
    {
      if ( (ii%NQNumbers_stcf==0) ) cout << ii/NQNumbers_stcf << ": ";
      //cout << ii << " -  " << StCameFromQNumbers[ii] << " ";
      cout << StCameFromQNumbers[ii] << " ";
      if ( ((ii+1)%NQNumbers_stcf==0) ) cout << endl;
    }

}
///////////////////
//////////////
void CNRGbasisarray::PrintBlockBasis(int iblock){

  CNRGarray::PrintBlockQNumbers(iblock);

  int iBeg=GetBlockLimit(iblock,0);
  int iEnd=GetBlockLimit(iblock,1);
  cout << " Beg: " << iBeg << " End: " << iEnd << endl;
  for (int ii=iBeg;ii<=iEnd;ii++){
    cout << ii << " ";
    if (dEn.size()>0)
      cout << scientific << "Eold = " << dEn[ii] << "  ";
    if (iType.size()>0)
      cout << fixed << "type = " << iType[ii] << "  ";
    if (StCameFrom.size()>0)
      cout << fixed << "StCFrom = " << StCameFrom[ii]  << "  ";
    if (iDegen.size()>ii)
      cout << fixed << "iDeg = " << iDegen[ii] << "  ";
    cout << resetiosflags (ios_base::floatfield);
    if (StCameFromQNumbers.size()>0)
      {
	cout << "SCF_QNbrs: ";
	for (int jj=ii*NQNumbers_stcf;jj<(ii+1)*NQNumbers_stcf;jj++)
	  cout << StCameFromQNumbers[jj] << " ";
      }
    cout << endl;

  }
  // end loop in ii

}
/////////////
void  CNRGbasisarray::SetSCFfromdEn(){

  // Sets StCameFrom

  StCameFrom.clear();
  for (int ii=0;ii<dEn.size();ii++) StCameFrom.push_back(ii);

}

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

//////////////
int CNRGbasisarray::GetBlockSizeBC(int iblock){

  if(BlockBegEndBC.size()==0){return(GetBlockSize(iblock));}

  return(BlockBegEndBC[2*iblock+1]-BlockBegEndBC[2*iblock]+1);

}

//////////////
int CNRGbasisarray::GetBlockFromStBC(int ist,int &pos_in_block){

  // Find which block ist belongs in the BC structure:
  // FInds iblock such that
  // BlockBegEnd[2*iblock]<=ist<=BlockBegEnd[2*iblock+1]

  if (BlockBegEndBC.size()==0){
    int auxst=CNRGarray::GetBlockFromSt(ist,pos_in_block);
    return(auxst);
  }
  else{

    int iblock=0;

    while (ist>BlockBegEndBC[2*iblock+1]) {iblock++;}

    pos_in_block=ist-BlockBegEndBC[2*iblock];

    return(iblock);
  }
  // end if else
}

//////////////
void CNRGbasisarray::SetBlockBegEndEigVec(){

  BlockBegEndEigVec.clear();

  int Nbls=(int)BlockBegEndBC.size()/2;
  if (Nbls==0) return;

  int aux=0;
  for (int ibl=0;ibl<Nbls;ibl++)
    {
      BlockBegEndEigVec.push_back(aux);
      aux+=GetBlockSizeBC(ibl)*GetBlockSizeBC(ibl)-1;
      BlockBegEndEigVec.push_back(aux);
      aux++;
    }

}

/////////////
void CNRGbasisarray::RemoveBlock(int iblock){

  // Checks StCameFrom

  if ( (dEn.size()==0)||( (dEigVec.size()==0)&&(cEigVec.size()==0) )
       ||(StCameFrom.size()==0) ){
      cout << "Cant remove block: " << iblock << endl;
      return;
    }
  //CNRGbasisarray::PrintAll();

  // Erase elements in dEn, dEigVec, StCameFrom, Kept

  dEn.erase(dEn.begin()+GetBlockLimit(iblock,0),
	    dEn.begin()+GetBlockLimit(iblock,1)+1);

  StCameFrom.erase(StCameFrom.begin()+GetBlockLimit(iblock,0),
		   StCameFrom.begin()+GetBlockLimit(iblock,1)+1);

  Kept.erase(Kept.begin()+GetBlockLimit(iblock,0),
	    Kept.begin()+GetBlockLimit(iblock,1)+1);

  // Watch out
  if (dEigVec.size()>0)
  dEigVec.erase(dEigVec.begin()+BlockBegEndEigVec[2*iblock],
		dEigVec.begin()+BlockBegEndEigVec[2*iblock+1]+1);

  if (cEigVec.size()>0)
  cEigVec.erase(cEigVec.begin()+BlockBegEndEigVec[2*iblock],
		cEigVec.begin()+BlockBegEndEigVec[2*iblock+1]+1);

  // Erase elements in QNumbers

  QNumbers.erase(QNumbers.begin()+NQNumbers*iblock,
		 QNumbers.begin()+NQNumbers*(iblock+1));

  // Re-arrange BlockBegEnd
  // Re-arrange BlockBegEndEigVec

  for (int ibl1=iblock;ibl1<NumBlocks();ibl1++)
    {
      int Size_next=GetBlockSize(ibl1+1);
      BlockBegEnd[2*ibl1+1]=BlockBegEnd[2*ibl1]+Size_next-1;
      BlockBegEnd[2*ibl1+2]=BlockBegEnd[2*ibl1]+Size_next;

      int Size_next2=Size_next*Size_next;
      BlockBegEndEigVec[2*ibl1+1]=BlockBegEndEigVec[2*ibl1]+Size_next2-1;
      BlockBegEndEigVec[2*ibl1+2]=BlockBegEndEigVec[2*ibl1]+Size_next2;

    }
  BlockBegEnd.pop_back();
  BlockBegEnd.pop_back();

  BlockBegEndEigVec.pop_back();
  BlockBegEndEigVec.pop_back();

  // Erase elements in BlockBegEndBC

  BlockBegEndBC.erase(BlockBegEndBC.begin()+2*iblock,
		      BlockBegEndBC.begin()+2*(iblock+1));

}

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

/////////////
void CNRGbasisarray::RemoveState(int ist){

  // Now it does cut egenvectors as well!!

  // Checks vectors

  if ( (dEn.size()==0)||(StCameFrom.size()==0) )
    return;

  int pos_in_bl;
  int iblock=CNRGarray::GetBlockFromSt(ist,pos_in_bl);
  int bl_size=CNRGarray::GetBlockSize(iblock);
  int bl_sizeBC=GetBlockSizeBC(iblock);

  if (BlockBegEnd[2*iblock]==
      BlockBegEnd[2*iblock+1]) CNRGbasisarray::RemoveBlock(iblock);
  else{

    dEn.erase(dEn.begin()+ist);

    StCameFrom.erase(StCameFrom.begin()+ist);

    Kept.erase(Kept.begin()+ist);

    // This doesn't work:
    //       vector<double>::iterator it=dEigVec.begin()+GetBlockLimitEigv(iblock,0)+bl_size*pos_in_bl;

    //       dEigVec.erase(it,it+bl_size);
    //
    //
    // bl_size should be the original block size but
    // no information on the original block length is available anymore.
    //
    // Needs a "BlockBegEndBC" or something.
    //
    // But we won't be needing the eigenvectors to generate the basis anyway.
    //

    // Now this should work:

    vector<double>::iterator it=dEigVec.begin()+
      BlockBegEndEigVec[2*iblock]+bl_sizeBC*pos_in_bl;

    vector<complex<double> >::iterator cit=cEigVec.begin()+
      BlockBegEndEigVec[2*iblock]+bl_sizeBC*pos_in_bl;

//     if (it+bl_sizeBC>dEigVec.end()){
    if ( (it+bl_sizeBC>dEigVec.end())&&(cit+bl_sizeBC>cEigVec.end()) ) {
      cout << " Ops, Problems in removing eigvec! ";
      cout << "Removing section of EigVec. bl_sizeBC = " << bl_sizeBC;
      cout << " BegEigVec at : " << BlockBegEndEigVec[2*iblock];
      cout << " EndEigVec at : " << BlockBegEndEigVec[2*iblock+1];
      cout << " bl_sizeBC*pos_in_bl : " << bl_sizeBC*pos_in_bl;
      cout << endl;
      exit(0);
    }

    if (it+bl_sizeBC<=dEigVec.end())
      dEigVec.erase(it,it+bl_sizeBC);
    if (cit+bl_sizeBC<=cEigVec.end())
      cEigVec.erase(cit,cit+bl_sizeBC);

    //Update BlockBegEnd
    //Update BlockBegEndEigVec
    BlockBegEnd[2*iblock+1]-=1;
    BlockBegEndEigVec[2*iblock+1]-=bl_sizeBC;

    for (int ibl1=iblock+1;ibl1<NumBlocks();ibl1++){
      BlockBegEnd[2*ibl1]-=1;
      BlockBegEnd[2*ibl1+1]-=1;

      BlockBegEndEigVec[2*ibl1]-=bl_sizeBC;
      BlockBegEndEigVec[2*ibl1+1]-=bl_sizeBC;
    }

  }

}

/////////////
void CNRGbasisarray::RemoveStatesFromBlock(int iblock, int ist1,int ist2){

  // Now it does cut egenvectors as well!!
  // Similar as RemoveState but does that in blocks

  // Checks vectors

  if ( (dEn.size()==0)||(StCameFrom.size()==0) )
    return;

  int NstGone=ist2-ist1+1;

  // Make sure that ist1 and ist2 belong to iblock
  int pos_in_bl[2];
  int ibl_check1=CNRGarray::GetBlockFromSt(ist1,pos_in_bl[0]);
  int ibl_check2=CNRGarray::GetBlockFromSt(ist2,pos_in_bl[1]);
  if ( (ibl_check1!=iblock)||(ibl_check2!=iblock) ){
    cout << " Error in RemoveStatesFromBlock: ist1, ist2  are not within block " << endl;
    return;
  }

  int bl_size=CNRGarray::GetBlockSize(iblock);
  int bl_sizeBC=GetBlockSizeBC(iblock);

  if (BlockBegEnd[2*iblock]==
      BlockBegEnd[2*iblock+1]) CNRGbasisarray::RemoveBlock(iblock);
  else{

    dEn.erase(dEn.begin()+ist1,dEn.begin()+ist2+1); // Check if works

    StCameFrom.erase(StCameFrom.begin()+ist1,StCameFrom.begin()+ist2+1);

    Kept.erase(Kept.begin()+ist1,Kept.begin()+ist2+1);
    //
    // Now the tricky part: the eigenvector
    //

    //
    // Iterator set at begining of first vector
    //
    vector<double>::iterator it=dEigVec.begin()+
      BlockBegEndEigVec[2*iblock]+bl_sizeBC*pos_in_bl[0];
    vector<complex<double> >::iterator cit=cEigVec.begin()+
      BlockBegEndEigVec[2*iblock]+bl_sizeBC*pos_in_bl[0];

//     if (it+bl_sizeBC>dEigVec.end()){
    if ( (it+bl_sizeBC>dEigVec.end())&&(cit+bl_sizeBC>cEigVec.end()) ){
      cout << " Ops, Problems in removing eigvec! ";
      cout << "Removing section of EigVec. bl_sizeBC = " << bl_sizeBC;
      cout << " BegEigVec at : " << BlockBegEndEigVec[2*iblock];
      cout << " EndEigVec at : " << BlockBegEndEigVec[2*iblock+1];
      cout << " bl_sizeBC*pos_in_bl[0] : " << bl_sizeBC*pos_in_bl[0];
      cout << endl;
      return;
    }

    if (it+bl_sizeBC<=dEigVec.end())
      dEigVec.erase(it,it+bl_sizeBC*NstGone); // Should work

    if (cit+bl_sizeBC<=cEigVec.end())
      cEigVec.erase(cit,cit+bl_sizeBC*NstGone); // Should work

    // Re-struct
    //Update BlockBegEnd
    //Update BlockBegEndEigVec
    BlockBegEnd[2*iblock+1]-=NstGone;
    BlockBegEndEigVec[2*iblock+1]-=bl_sizeBC*NstGone;

    for (int ibl1=iblock+1;ibl1<NumBlocks();ibl1++){
      BlockBegEnd[2*ibl1]-=NstGone;
      BlockBegEnd[2*ibl1+1]-=NstGone;

      BlockBegEndEigVec[2*ibl1]-=bl_sizeBC*NstGone;
      BlockBegEndEigVec[2*ibl1+1]-=bl_sizeBC*NstGone;
    }

  }
  // If not RemoveBlock

}
///////////// April 09
int CNRGbasisarray::GetBlockLimitEigv(int iblock, int whichlimit){

  // Returns position in dEigVec corresponding to block iblock
  // USES BlockBegEndEigVec if available!!

  if (BlockBegEndEigVec.size()==0){
    return(CNRGarray::GetBlockLimitEigv(iblock,whichlimit));
  }
  else{
//    cout << "Overloaded GetBlockLimitEigv (CNRGbasisarray)" << endl;
    return(BlockBegEndEigVec[2*iblock+whichlimit]);
  }

}
///////////////
///////////// April 09
double CNRGbasisarray::GetEigVecComponent(int ist,int istbasis){

  // Overloads in the case of "cut" vectors
  // For "uncut" vectors only!
  // A CNRGbasisarray version for this one and for
  // GetBlockLimitEigv should do the job
  // in cut objects.
  //

  if  (dEigVec.size()==0){
    cout << "GetEigVecComponent: No dEigVec. Return 0." << endl;
    return(0.0);
  }

  int posBl;
  int posBlbasis;
  int ibl=GetBlockFromSt(ist,posBl);
  int iblbasis=GetBlockFromStBC(istbasis,posBlbasis);
  int bl_sizeBC=GetBlockSizeBC(ibl);

  //  cout << "GetEigVecComponent: Using overloaded version" << endl;

  if (ibl==iblbasis){
    int ieigv0=GetBlockLimitEigv(ibl,0);
    vector<double>::iterator it;
    it=dEigVec.begin()+ieigv0+posBl*bl_sizeBC+posBlbasis;
    return(*it);
  }
  else{return(0.0);}

}

///////////////
///////////////
boost::numeric::ublas::matrix<double> CNRGbasisarray::EigVecCut2BLAS(int iblock){

  // Converts the section in dEigVec corresponding to block iblock to uBLAS
  // Format: each eigenvector in a ROW!

  int Nst=GetBlockSize(iblock);
  int pos=BlockBegEndEigVec[2*iblock];

  int NstBC=GetBlockSizeBC(iblock);

  //cout << "NstBlock = " << Nst << "  Pos in dEigVec = " << pos << endl;

  boost::numeric::ublas::matrix<double> Maux(Nst,NstBC);

  // Let me try this:

  vector<double>::iterator it;

  it=dEigVec.begin()+pos;
  for (int ii=0;ii<Nst;ii++){
    for (int jj=0;jj<NstBC;jj++){
      Maux(ii,jj)=*it;
      it++;
    }
  }
  //return(boost::numeric::ublas::trans(Maux)); // In Columns
  return(Maux);          // In Rows

}
/// Now the complex one
boost::numeric::ublas::matrix<complex<double> > CNRGbasisarray::cEigVecCut2BLAS(int iblock){

  // Converts the section in cEigVec corresponding to block iblock to uBLAS
  // Format: each eigenvector in a ROW!

  int Nst=GetBlockSize(iblock);
  int pos=BlockBegEndEigVec[2*iblock];

  int NstBC=GetBlockSizeBC(iblock);

  //cout << "NstBlock = " << Nst << "  Pos in dEigVec = " << pos << endl;

  boost::numeric::ublas::matrix<complex<double> > Maux(Nst,NstBC);

  vector<complex<double> >::iterator it;

  it=cEigVec.begin()+pos;
  for (int ii=0;ii<Nst;ii++){
    for (int jj=0;jj<NstBC;jj++){
      Maux(ii,jj)=*it;
      it++;
    }
  }
  //return(boost::numeric::ublas::trans(Maux)); // In Columns
  return(Maux);          // In Rows (complex)

}

///////////////
boost::numeric::ublas::matrix<double> CNRGbasisarray::EigVecCut2BLAS(int iblock,
								     bool kp){

  // Converts the section in dEigVec corresponding to block iblock to uBLAS
  // Format: each eigenvector in a ROW!

  // The assumption: NO CUTTING WAS DONE in the basisarray object

  int Nst=GetBlockSize(iblock);
  int pos=BlockBegEndEigVec[2*iblock];

  int NstBC=GetBlockSizeBC(iblock); // Should be equal to Nst

  int Nstkp=CNRGarray::GetBlockSize(iblock,kp);

  //cout << "NstBlock = " << Nst << "  Pos in dEigVec = " << pos << endl;

  boost::numeric::ublas::matrix<double> Maux(Nstkp,Nstkp);
  // if Nst != NstBC, then we will hake a problem here:
  // this matrix will not be square

  vector<double>::iterator it;

  it=dEigVec.begin()+pos;
  int istkp=0;
  for (int ii=0;ii<Nst;ii++){
    int jstkp=0;
    for (int jj=0;jj<NstBC;jj++){
      if (CheckKept(iblock,ii,kp)){
	Maux(istkp,jstkp)=*it;
	jstkp++;
      }
      // if state kept==kp
      it++;
    }
    // end loop in components
    if (CheckKept(iblock,ii,kp)){istkp++;}
  }
  // end loop in states

  //return(boost::numeric::ublas::trans(Maux)); // In Columns
  return(Maux);          // In Rows

}

///////////////
///////////////
///////////////
///////////////
///////////// April 08
double CNRGbasisarray::GetStCameFromQNumberFromSt(int ist, int whichqn){

  int stcf=StCameFrom[ist];

  return(StCameFromQNumbers[NQNumbers_stcf*ist+whichqn]);

}
/////////////

///////////////
///////////// June 08
/////////////
void CNRGbasisarray::CopyBlock(int iblock,int NoCopies){

  // Checks StCameFrom

  if ( (NoCopies<1)||(NoCopies>100) )
    {
      cout << " CopyBlock : NoCopies not valid " << endl;
      return;
    }

  // Set iDegen if not set yet
  if (iDegen.size()==0)
    for (int ii=0;ii<Nstates();ii++) iDegen.push_back(0);

  // Set iType if not set yet
  if (iType.size()==0)
    for (int ii=0;ii<Nstates();ii++) iType.push_back(0);

  int BlSize=GetBlockSize(iblock);

  vector<double> dAux;
  vector<int> iAux,iAux2;

//   cout << " Bl Size = " << BlSize
//        << " Limit 0: " << GetBlockLimit(iblock,0)
//        << " Limit 1: " << GetBlockLimit(iblock,1)
//        << endl;

  dAux.assign(dEn.begin()+GetBlockLimit(iblock,0),
 	      dEn.begin()+GetBlockLimit(iblock,1)+1);

  iAux.assign(iType.begin()+GetBlockLimit(iblock,0),
 	      iType.begin()+GetBlockLimit(iblock,1)+1);

  iAux2.assign(StCameFrom.begin()+GetBlockLimit(iblock,0),
 	      StCameFrom.begin()+GetBlockLimit(iblock,1)+1);

  for (int ic=1;ic<=NoCopies;ic++)
    {
      // Copy to dEn
      dEn.insert(dEn.begin()+GetBlockLimit(iblock,1)+1,dAux.begin(),dAux.end());
      // Copy to iType
      iType.insert(iType.begin()+GetBlockLimit(iblock,1)+1,iAux.begin(),iAux.end());
      // Copy to StCameFrom
      StCameFrom.insert(StCameFrom.begin()+GetBlockLimit(iblock,1)+1,iAux2.begin(),iAux2.end());

      // Update iDegen

      iDegen.insert(iDegen.begin()+GetBlockLimit(iblock,1)+1,BlSize,ic);

      // Re-arrange BlockBegEnd, Get ready for next copy
      BlockBegEnd[2*iblock+1]+=BlSize;
      for (int ibl1=iblock+1;ibl1<NumBlocks();ibl1++)
	{
	  BlockBegEnd[2*ibl1]+=BlSize;
	  BlockBegEnd[2*ibl1+1]+=BlSize;
	}
      // end loop in blocks

    }
  iAux.clear();
  dAux.clear();

}

///////////////
/////////////
///////////// September 08
/////////////
void CNRGbasisarray::SetLastQNumber(vector<double> InputVec){

  // Based on the iDegen values, add QNumber and rearrange
  // dEn, QNumbers and BlockBegEnd. Good for phonons.
  // It will re-order the blocks according with iDegen values
  // (it may come down to a single state per block)

  if (InputVec.size()!=Nstates()){
    cout << "SetLastQNumber : Input vec size does not match" << endl;
    return;
  }

  // assume it is the latest qn

  vector<double> auxdEn;
  vector<double> auxQNumbers;
  vector<int> auxBlockBegEnd;
  vector<int> auxiDegen;

  vector<int> auxiType;
  vector<int> auxStCameFrom;

  vector<double>::iterator it0;
  vector<double>::iterator it1;

  vector<int>::iterator it_ideg;

  double *auxdQN=new double [NQNumbers];

  int NoBlocksOri=NumBlocks(); // will increase QNumbers so need this one
  int Nadded=0;

  // Add new blocks to QNumbers

  auxQNumbers=QNumbers;

  auxdQN[0]=0.0;
  for (int ibl=0;ibl<NoBlocksOri;ibl++){
    int ist0=GetBlockLimit(ibl,0);
    int ist1=GetBlockLimit(ibl,1);

    for (int ist=ist0;ist<=ist1;ist++){
      it0=QNumbers.begin()+(ibl+Nadded)*NQNumbers;
      it1=QNumbers.begin()+(ibl+Nadded+1)*NQNumbers;
      // 	  auxdQN[0]=(double)iDegen[ist];
      auxdQN[0]=InputVec[ist];
      QNumbers[(Nadded*NQNumbers)+((ibl+1)*NQNumbers)-1]=auxdQN[0];
      if (ist<ist1)
	{QNumbers.insert(it0,it0,it1);Nadded++;}
    }
    // end loop in states
  }
  // end loop in blocks

  FilterQNumbers();

  // Update Block structure

  auxdEn.clear();
  auxiDegen.clear();
  auxBlockBegEnd.clear();
  auxiType.clear();
  auxStCameFrom.clear();

  Nadded=0;

  // Loop in new block structure
  for (int ibl=0;ibl<NumBlocks();ibl++){
    for (int iqn=0;iqn<NQNumbers;iqn++){auxdQN[iqn]=GetQNumber(ibl,iqn);}

    // Loop in the old block structure
    auxBlockBegEnd.push_back(Nadded);
    for (int ibl1=0;ibl1<NoBlocksOri;ibl1++){
      bool ok_match=true;

      int ist0=GetBlockLimit(ibl1,0);
      int ist1=GetBlockLimit(ibl1,1);

      for (int iqn=0;iqn<NQNumbers-1;iqn++)
	if(dNEqual(auxdQN[iqn],auxQNumbers[ibl1*NQNumbers+iqn]))
	  ok_match=false;
      if (ok_match){
	//cout << " Blocks Match: " << " ibl1= " << ibl1 << " ibl= " << ibl  << endl;
	for (int ist=ist0;ist<=ist1;ist++){
	  // 		  if (dEqual(auxdQN[NQNumbers-1],(double)iDegen[ist]))
	  if (dEqual(auxdQN[NQNumbers-1],InputVec[ist])){

	    if (ist<dEn.size()) auxdEn.push_back(dEn[ist]);
	    if (ist<iDegen.size()) auxiDegen.push_back(iDegen[ist]);
	    if (ist<iType.size()) auxiType.push_back(iType[ist]);
	    if (ist<StCameFrom.size()) auxStCameFrom.push_back(StCameFrom[ist]);

	    Nadded++;
	    //cout << " Adding ist = " << ist << " to block " << ibl << endl;
	  }
	}
	// end loop in ist in matchin blocks
      }
      // if match
    }
    // loop in old blocks

    auxBlockBegEnd.push_back(Nadded-1);
  }
  // loop in new blocks

  // Update other vectors
  dEn.clear();
  BlockBegEnd.clear();
  iDegen.clear();
  iType.clear();
  StCameFrom.clear();

  dEn=auxdEn;
  BlockBegEnd=auxBlockBegEnd;
  iDegen=auxiDegen;

  iType=auxiType;
  StCameFrom=auxStCameFrom;

  delete[] auxdQN;

}

///////////

//
/////////////
///////////// February 09
/////////////

void CNRGbasisarray::SaveBin(char arqname[]){

  CNRGarray::SaveBin(arqname);

  vector<double>::iterator dit;
  vector<int>::iterator iit;

  ofstream OutFile(arqname, ios::out | ios::binary | ios::app);
  double daux;
  int iaux;

  if (!OutFile){cout << "SaveBin: Cannot save data in " << arqname << endl; return;}

  // Save iType
  iaux=iType.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(iit=iType.begin();iit<iType.end();iit++){
    OutFile.write(reinterpret_cast<char*>(&(*iit)), sizeof(int));
  }

  // Save StCameFrom
  iaux=StCameFrom.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(iit=StCameFrom.begin();iit<StCameFrom.end();iit++){
    OutFile.write(reinterpret_cast<char*>(&(*iit)), sizeof(int));
  }

  // Save BlockBegEndBC
  iaux=BlockBegEndBC.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(iit=BlockBegEndBC.begin();iit<BlockBegEndBC.end();iit++){
    OutFile.write(reinterpret_cast<char*>(&(*iit)), sizeof(int));
  }

  // Save BlockBegEndEigVec
  iaux=BlockBegEndEigVec.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(iit=BlockBegEndEigVec.begin();iit<BlockBegEndEigVec.end();iit++){
    OutFile.write(reinterpret_cast<char*>(&(*iit)), sizeof(int));
  }
  // Save NQNumbers_stcf
  OutFile.write((char*)&NQNumbers_stcf, sizeof(int));
  // Save QNumbers_stcf
  iaux=StCameFromQNumbers.size();
  OutFile.write((char*)&iaux, sizeof(int));
  for(dit=StCameFromQNumbers.begin();dit<StCameFromQNumbers.end();dit++){
    daux=*dit;
    OutFile.write(reinterpret_cast<char*>(&daux), sizeof(double));
  }

  OutFile.close();

  if (!OutFile.good()){cout << "SaveBin: error saving" << endl;}

}
///

void CNRGbasisarray::ReadBin(char arqname[]){

  // ifstream
  ifstream InFile;

  InFile.open(arqname, ios::in | ios::binary);
  //InFile.seekg(0,ios::beg);

  if (!InFile){cout << "ReadBin: Cannot read data in " << arqname << endl; return;}

  CNRGbasisarray::ReadBinInStream(InFile);

  InFile.close();

  if (!InFile.good()){cout << "ReadBin: overall error reading" << endl;}

}

/////
void CNRGbasisarray::ReadBinInStream(ifstream &InFile){

  int isize,iaux, iaux2;
  double daux;

  CNRGarray::ReadBinInStream(InFile);

   // Read iType
  iType.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&iaux2), sizeof(int));
    iType.push_back(iaux2);
  }
  // Read StCameFrom
  StCameFrom.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&iaux2), sizeof(int));
    StCameFrom.push_back(iaux2);
  }
  // Read BlockBegEndBC BlockBegEigVec
  BlockBegEndBC.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&iaux2), sizeof(int));
    BlockBegEndBC.push_back(iaux2);
  }
  // Read BlockBegEndEigVec
  BlockBegEndEigVec.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&iaux2), sizeof(int));
    BlockBegEndEigVec.push_back(iaux2);
  }
  // Read NQNumbers_stcf
  InFile.read((char*)&NQNumbers_stcf, sizeof(int));
  // Read QNumbers_stcf
  StCameFromQNumbers.clear();
  InFile.read((char*)&isize, sizeof(int));
  for(iaux=0;iaux<isize;iaux++){
    InFile.read(reinterpret_cast<char*>(&daux), sizeof(double));
    StCameFromQNumbers.push_back(daux);
  }

}
/////////////

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