#define BDMLIB // not an ideal way to prevent double registration of UI factories...
#include "base/bdmbase.h"
#include "base/user_info.h"
#include "stat/exp_family.h"
#include "stat/emix.h"
#include "itpp_ext.h"
#include "mpdf_harness.h"
#include "mat_checks.h"
#include "UnitTest++.h"

using namespace bdm;

const double epsilon = 0.00001;

namespace UnitTest
{

inline void CheckClose(TestResults &results, const itpp::vec &expected,
		       const itpp::vec &actual, double tolerance,
		       TestDetails const& details) {
    if (!AreClose(expected, actual, tolerance)) { 
        MemoryOutStream stream;
        stream << "Expected " << expected << " +/- " << tolerance << " but was " << actual;

        results.OnTestFailure(details, stream.GetText());
    }
}

inline void CheckClose(TestResults &results, const itpp::mat &expected,
		       const itpp::mat &actual, double tolerance,
		       TestDetails const& details) {
    if (!AreClose(expected, actual, tolerance)) { 
        MemoryOutStream stream;
        stream << "Expected " << expected << " +/- " << tolerance << " but was " << actual;

        results.OnTestFailure(details, stream.GetText());
    }
}

}

TEST(test_mepdf) {
    RV::clear_all();
    UIFile in("mepdf.cfg");
    Array<mpdf_harness *> input;
    UI::get(input, in, "data");
    int sz = input.size();
    CHECK(sz > 0);
    for (int i = 0; i < sz; ++i) {
        input(i)->test();
    }
}

TEST(test_mepdf_sample) {
    // Setup model
    vec mu("1.1 -1");
    ldmat R(mat("1 -0.5; -0.5 2"));

    RV x("{x }");
    RV y("{y }");

    enorm<ldmat> E;
    E.set_rv(concat(x, y));
    E.set_parameters(mu, R);

    epdf *Mg = E.marginal(y);
    CHECK_CLOSE(vec("-1"), Mg->mean(), epsilon);

    // putting them back together
    mpdf *Cn = E.condition(x);
    mepdf mMg(Mg);
    Array<mpdf *> A(2);
    A(0) = Cn;
    A(1) = &mMg;
    mprod mEp(A);

    int n = 1000;
    mat smp = mEp.samplecond(vec(0), n);
    vec emu = sum(smp, 2) / n;
    CHECK_CLOSE(mu, emu, 0.3);

    mat er = (smp * smp.T()) / n - outer_product(emu, emu);
    CHECK_CLOSE(R.to_mat(), er, 0.3);

    // test of pdflog at zero
    vec zero(0);
    vec zero2("0 0");
    CHECK_CLOSE(E.evallog(zero2), mEp.evallogcond(zero2, zero), epsilon);
}
