#define BDMLIB // not an ideal way to prevent double registration of UI factories...
#include "stat/exp_family.h"
#include "epdf_harness.h"
#include "mat_checks.h"
#include "test_util.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());
    }
}

}

template<>
const ParticularUI<enorm<ldmat> > &ParticularUI<enorm<ldmat> >::factory(
    ParticularUI<enorm<ldmat> >("enorm<ldmat>"));

template<>
const ParticularUI<enorm<fsqmat> > &ParticularUI<enorm<fsqmat> >::factory(
    ParticularUI<enorm<fsqmat> >("enorm<fsqmat>"));

TEST(test_enorm) {
    RV::clear_all();
    UIFile in("enorm.cfg");
    Array<epdf_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_enorm_sample) {
    RNG_randomize();

    // 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);

    int n = 1000;
    vec ll(n);
    mat smp = E.sample(1000);
    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);
}
